キャラクタ・アニメーションは、
用意した複数の画像を一定時間ごとに切り替えて表示することにより行われる。では、どのようにプログラムすればよいだろうか?
- キャラクタ情報初期化処理で、
最初に表示するコマ数をセット。- キャラクタ描画時、
表示したいコマに対応する矩形を描画。 一定時間が経過したら、コマ数を増やす。最大コマ数に達したら、0に戻す。ゲーム中、特定の場所に静止しているゴキブリを、500ミリ秒ごとにアニメーションさせるプログラムを作成する。フローチャートは次のようになる。
![]()
このフローチャートを元に、プログラミングする。
FPSを表示するために、WinMain.cppでnowTickCount変数に時間を格納している。この変数の値を他のソースで利用できるようにすれば、毎回timeGetTime関数を利用する必要がない。まず、
WinMain.cppを次のように修正する。
//============================================================================= // 2Dゲーム基本プログラム3(第6章キャラクタ表示テスト用プログラム) //============================================================================= #include "common.h" //----------------------------------------------------------------------------- // プロトタイプ宣言 //----------------------------------------------------------------------------- static LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM); static HWND InitApp(HINSTANCE, int); static void UpdateFrame(HWND); //----------------------------------------------------------------------------- // 外部変数(本体) //----------------------------------------------------------------------------- BYTE g_FrameNo = START_INIT; // フレーム選択用 BYTE KeyTbl[256]; // キー情報 RECT ScreenRect = {0, 0, 640, 480}; // ウィンドウの矩形DWORD nowTickCount; // 現在の時間//----------------------------------------------------------------------------- // グローバル変数 //----------------------------------------------------------------------------- static char szWinName[] = "Skeleton"; // ウィンドウクラス名 static char szTitle[] = "2Dゲーム基本プログラム3"; // ウィンドウタイトル名static DWORD backTickCount; // 時間制御用・ ・ ・※グローバル変数として宣言していたnowTickCount変数を、外部変数として宣言しなおした
次に、nowTickCount変数を他のソースで利用できるよう、
common.hを修正する。
・ ・ ・ //----------------------------------------------------------------------------- // 外部変数(本体は別ソース) //----------------------------------------------------------------------------- ・ ・ ・extern DWORD nowTickCount;
アニメーションは通常キャラクタごとに行うため、
キャラクタ構造体にアニメーション用の変数を用意する。common.hを次のように修正する。
//============================================================================= // プロジェクト内で共通に読み込まれるヘッダ・ファイル //============================================================================= //----------------------------------------------------------------------------- // インクルード・ファイル //----------------------------------------------------------------------------- ・ ・ ・ //----------------------------------------------------------------------------- // マクロ定義 //----------------------------------------------------------------------------- /* ゲーム進行状況判断用 */ #define START_INIT 0 #define START_FRAME 1 #define GAME_INIT 10 #define GAME_FRAME 11 /* キャラクタ情報用 */ #define DIRECTION_MAX 5 // キャラクタの向き情報の最大数#define ANIMATION_MAX 5 // キャラクタのアニメーション数の最大#define ANIMATION_TIME 500 // キャラクタアニメーションの単位(ミリ秒)//----------------------------------------------------------------------------- // 列挙型 //----------------------------------------------------------------------------- /* キャラクタの向きを格納する変数の型 */ typedef enum tarKEYSTATE { STOP, LEFT, RIGHT, UP, DOWN } KEYSTATE; //----------------------------------------------------------------------------- // 構造体 //----------------------------------------------------------------------------- /* キャラクタ・ステータス情報 */ typedef struct tarSTATUS { int x, y; // 表示座標 RECT rect[DIRECTION_MAX][ANIMATION_MAX]; // 画像の矩形 RECT hitrect; // 当たり判定用の矩形 int move_x, move_y; // 移動量 int width, height; // キャラクタの幅と高さ KEYSTATE direction; // 現在向いている方向(0:STOP 1:LEFT 2:RIGHT 3:UP 4:DOWN)DWORD AnimeTime; // アニメーション後経過時間int AnimeMax; // アニメーション数int AnimeNo; // 現在のアニメーション番号} STATUS; //----------------------------------------------------------------------------- // 外部変数(本体は別ソース) //----------------------------------------------------------------------------- /* DirectDrawオブジェクト関係 */ ・ ・ ・※矩形の配列が、方向と方向に対するアニメーションで2次元配列になっている
キャラクタ構造体に3つのメンバ「AnimeTime」「AnimeMax」「AnimeNo」が増えたため、初期化処理関数を修正する。
【プロトタイプ宣言】
static void StatusInit(STATUS*, int, int, int, int, int, int, int, int, int, KEYSTATE, DWORD, int, int);【StatusInit関数本体】
//----------------------------------------------------------------------------- // 関数名 : StatusInit() // 機能概要: ステータス情報初期化 //----------------------------------------------------------------------------- static void StatusInit( STATUS *status, // キャラクタ構造体変数のポインタ int left, int top, int right, int bottom, // 画像の矩形(一枚目) int x, int y, // 表示座標の初期値 int move_x, int move_y, // 移動量の初期値 int direction_count, KEYSTATE direction, // 方向の数と、方向の初期値DWORD AnimeTime, int AnimeMax, int AnimeNo // 経過時間、最大表示数、表示番号) { int i, j; // キャラクタの幅と高さ status->width = right - left; status->height = bottom - top; // キャラクタ表示位置 status->x = x; status->y = y; // キャラクタの移動量 status->move_x = move_x; status->move_y = move_y;// アニメーション後経過時間と、アニメーション数、現在のアニメーション番号status->AnimeTime = AnimeTime;status->AnimeMax = AnimeMax;status->AnimeNo = AnimeNo;// アニメーションと向きに対する矩形、現在の向きfor (i=0 ; i<direction_count ; i++) {for (j=0 ; j<AnimeMax ; j++){status->rect[i][j].left = left+ status->width * j; status->rect[i][j].top = top + status->height * i; status->rect[i][j].right = status->rect[i][j].left + status->width; status->rect[i][j].bottom = status->rect[i][j].top + status->height;}} status->direction = direction; }※1つの方向に対して5つのアニメーション用画像が横に配置されているため、矩形を計算で求めることができる。
5-3で修正した初期化用関数を使うため、初期化処理を修正する。
ゴキブリの画像を上向きで2枚使い、初期値は1枚目とする。
//----------------------------------------------------------------------------- // 関数名 : GameInit() // 機能概要: スタート処理初期化 //----------------------------------------------------------------------------- void GameInit(HWND hWnd) { //------------------------------------------------------- 各変数の初期化 /* 自キャラ(カーソルキーで移動) */ StatusInit(&MyChara, 0, 0, 40, 40, 310, 230, 1, 1, 5, STOP, nowTickCount, 1, 0); /* ゴキブリ(動かない) */ StatusInit(&Goki, 64, 394, 96, 426, 400, 300, 0, 0, 1, STOP, nowTickCount, 2, 0); //------------------------------------------------------- フレームナンバーセット g_FrameNo = GAME_FRAME; }※自キャラはアニメーションを行わないので、アニメーション数を1に、初期値を0(配列は0から始まる)をセット。
※ゴキブリは2枚の画像でアニメーションを行うので、アニメーション数を2に、初期値を0にセット。
キャラクタをアニメーションさせる関数「CharAnime()」を作成する。ゴキブリは500ミリ秒ごとにアニメーションする(画像が切り替わる)ように設定する。
【プロトタイプ宣言】
static void CharAnime(void); 【CharaAnime関数本体】
//----------------------------------------------------------------------------- // 関数名 : CharAnime() // 機能概要: キャラクタアニメーション処理 //----------------------------------------------------------------------------- static void CharAnime(void) { /* ゴキブリのアニメーション */ if ( nowTickCount - Goki.AnimeTime >= ANIMATION_TIME ) { // コマ数を進める Goki.AnimeNo++; if (Goki.AnimeNo >= Goki.AnimeMax) Goki.AnimeNo = 0; // アニメーション時間をクリアする Goki.AnimeTime = nowTickCount; } }
5-5で作成したキャラクタアニメーション処理関数を利用するため、
GameFrame関数を修正する。
//----------------------------------------------------------------------------- // 関数名 : GameFrame() // 機能概要: ゲーム画面更新処理 //----------------------------------------------------------------------------- void GameFrame(HWND hWnd) { ScreenOut(); // 描画処理 CharMove(); // キャラクタ移動処理CharAnime(); // キャラクタアニメーション処理ScreenHitCheck(); // キャラクタがゲーム画面をはみ出しているときの処理 CharHitCheck(); // キャラクタ同士の当たり判定&処理 // F1キーが押されたら、スタート画面に戻る(削除予定) if (KeyTbl[VK_F1] & 0x80) { g_FrameNo = START_INIT; return; } }
描画用画像の矩形は2次元配列になったため、描画処理を修正する。
//----------------------------------------------------------------------------- // 関数名 : ScreenOut() // 機能概要: ゲーム画像描画処理 //----------------------------------------------------------------------------- static void ScreenOut(void) { HRESULT hRet; /* ゲーム画面用背景 */ hRet = g_pDDSBack->BltFast(0, 0, g_pDDSGame, &ScreenRect, DDBLTFAST_NOCOLORKEY); if (hRet != DD_OK) return; /* 天使 */ hRet = g_pDDSBack->BltFast(MyChara.x, MyChara.y, g_pDDSChara, &MyChara.rect[MyChara.direction][MyChara.AnimeNo], DDBLTFAST_SRCCOLORKEY); if (hRet != DD_OK) return; /* ゴキブリ */ hRet = g_pDDSBack->BltFast(Goki.x, Goki.y, g_pDDSChara, &Goki.rect[Goki.direction][Goki.AnimeNo], DDBLTFAST_SRCCOLORKEY); if (hRet != DD_OK) return; }
上記全ての修正を行い、ゴキブリがアニメーションするかどうかを確認する。