カーソルキーが何も押されていないときは「静止画像」を、右が押されていたら「右向きの画像」というように、
移動方向によって表示する画像を切り替えるにはどうすればよいだろうか?
いろいろな方法があるが、ここでは配列を使った方法を紹介する。手順は次の通り。
- キャラクタ情報に、「
現在向いている方向を格納する変数」「向きごとの矩形を格納する配列」を持つ。- キャラクタ情報初期化処理で、向きごとの矩形情報(静止、上、下、左、右)を配列に格納する。
- 初期化処理時、向きの個数と、向きの初期値をセットする。
- キャラクタ画像描画時、向いている方向によって画像の矩形(配列の要素番号)を変える。
- ゲーム処理時、カーソルキーの状態を調べ、キャラクタ情報に格納する。
common.h内に作成した
キャラクタ構造体を次のように修正する。
typedef struct tarSTATUS { int x, y; // 表示座標 RECT rect[5]; // 画像の矩形 RECT hitrect; // 当たり判定用の矩形 int move_x, move_y; // 移動量 int width, height; // キャラクタの幅と高さint direction; // 現在向いている方向(0:STOP 1:LEFT 2:RIGHT 3:UP 4:DOWN)} STATUS;
- 矩形は最大5種類用意するため、
rect変数を要素数5の配列に変更- 現在の向きを保持する
direction変数を追加
方向分の矩形をセットできるよう、Game.cpp内を修正する。
- キャラクタ情報初期化関数「StatusInit」のプロトタイプ宣言を次のように修正(引数を2つ増やす)。
static void StatusInit(STATUS*, int, int, int, int, int, int, int, int , 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, int direction // 方向の数と、方向の初期値) { int i; // キャラクタの幅と高さ status->width =right - left; status->height =bottom - top; // キャラクタ表示位置 status->x = x; status->y = y; // キャラクタの移動量 status->move_x = move_x; status->move_y = move_y; // 向きの数と、向きに対する矩形for (i=0 ; i<direction_count ; i++){status->rect[i].left = left;status->rect[i].top = top + status->height * i;status->rect[i].right = status->rect[i].left + status->width;status->rect[i].bottom = status->rect[i].top + status->height;}status->direction = direction;}
- 方向別の画像が、静止の画像(基準)から下へ均等に配置されているため、
キャラクタの高さ×向き(0〜4)で、方向別の矩形を求めることができる。
初期化用関数を呼び出す際、「向きの数」「向きの初期値」を与える。
/* 自キャラ(カーソルキーで移動) */ StatusInit(&MyChara, 0, 0, 40, 40, 310, 230, 1, 1, 5, 0); /* ゴキブリ(動かない) */ StatusInit(&Goki, 64, 394, 96, 426, 400, 300, 0, 0, 1, 0);
- 自キャラは上下左右+停止状態の方向別に画像を切り替えるため、向きの数に「5」を、向きの初期値に「0(静止)」を与える。
- 敵キャラは動かないため、向きの数に「1」を、向きの初期値に「0(静止)」を与える。
画像表示時、方向の数値により、表示する矩形を切り替える。
/* 天使 */ hRet = g_pDDSBack->BltFast(MyChara.x, MyChara.y, g_pDDSChara, &MyChara.rect[MyChara.direction], DDBLTFAST_SRCCOLORKEY); if (hRet != DD_OK) return; /* ゴキブリ */ hRet = g_pDDSBack->BltFast(Goki.x, Goki.y, g_pDDSChara, &Goki.rect[Goki.direction], DDBLTFAST_SRCCOLORKEY); if (hRet != DD_OK) return;
- rect配列のうち、
direction番目の配列の値を利用して表示する。
(敵キャラは常に0番目の配列)
カーソルキーの状態によって向きを変えるため、移動処理を次のように修正する。
//----------------------------------------------------------------------------- // 関数名 : CharMove() // 機能概要: キャラクタ移動処理 //----------------------------------------------------------------------------- static void CharMove(void) { /* 天使の移動 */MyChara.direction = 0;if (KeyTbl[VK_LEFT] & 0x80){MyChara.x -= MyChara.move_x;MyChara.direction = 1;}if (KeyTbl[VK_RIGHT] & 0x80){MyChara.x += MyChara.move_x;MyChara.direction = 2;}if (KeyTbl[VK_UP] & 0x80){MyChara.y -= MyChara.move_y;MyChara.direction = 3;}if (KeyTbl[VK_DOWN] & 0x80){MyChara.y += MyChara.move_y;MyChara.direction = 4;}}
- カーソルキーの状態を調べる前に0を入れておかないと、
何も押していないときに静止状態(0)に戻らないことに注意!!
(a) 〜 (e)全ての修正を行い、正常に動くかどうかを確認する。
[ 実行結果サンプル ]
方向別に表示画像が変わるようにはなったが、1がLEFTであるとか3がUPであるなど、
数値に意味をつけて覚えるのは分かりずらい。そこで、enum型を利用して次のように改造すると、見やすくなる。
- common.hを次のように修正
//----------------------------------------------------------------------------- // 列挙型 //-----------------------------------------------------------------------------/* キャラクタの向きを格納する変数の型 */typedef enum tarKEYSTATE {STOP, LEFT, RIGHT, UP, DOWN} KEYSTATE;//----------------------------------------------------------------------------- // 構造体 //----------------------------------------------------------------------------- /* キャラクタ・ステータス情報 */ typedef struct tarSTATUS { int x, y; // 表示座標 RECT rect[5]; // 画像の矩形 RECT hitrect; // 当たり判定用の矩形 int move_x, move_y; // 移動量 int width, height; // キャラクタの幅と高さKEYSTATEdirection; // 現在向いている方向(0:STOP 1:LEFT 2:RIGHT 3:UP 4:DOWN) } STATUS;- Game.cppのキャラクタ情報初期化関数の最後の引数のデータ型をintから
KEYSTATEに変更(プロトタイプ宣言と関数本体)
//----------------------------------------------------------------------------- // プロトタイプ宣言(ソース内でしか使わないもの) //----------------------------------------------------------------------------- static void StatusInit(STATUS*, int, int, int, int, int, int, int, int, int ,KEYSTATE); //----------------------------------------------------------------------------- // 関数名 : 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,KEYSTATEdirection // 方向の数と、方向の初期値 )- 初期化で与える値を、0から
STOPに変更(自キャラと敵キャラ)
void GameInit(HWND hWnd) { //------------------------------------------------------- 各変数の初期化 /* 自キャラ(カーソルキーで移動) */ StatusInit(&MyChara, 0, 0, 40, 40, 310, 230, 1, 1, 5,STOP); /* ゴキブリ(動かない) */ StatusInit(&Goki, 64, 394, 96, 426, 400, 300, 0, 0, 1,STOP);- CharaMove関数で、0〜4をそれぞれ「
STOP」「LEFT」「RIGHT」「UP」「DOWN」に変更
//----------------------------------------------------------------------------- // 関数名 : CharMove() // 機能概要: キャラクタ移動処理 //----------------------------------------------------------------------------- static void CharMove(void) { /* 天使の移動 */ MyChara.direction =STOP; if (KeyTbl[VK_LEFT] & 0x80) { MyChara.x -= MyChara.move_x; MyChara.direction =LEFT; } if (KeyTbl[VK_RIGHT] & 0x80) { MyChara.x += MyChara.move_x; MyChara.direction =RIGHT; } if (KeyTbl[VK_UP] & 0x80) { MyChara.y -= MyChara.move_y; MyChara.direction =UP; } if (KeyTbl[VK_DOWN] & 0x80) { MyChara.y += MyChara.move_y; MyChara.direction =DOWN; }これにより、0〜4の数値ではなく、STOP、LEFTなど意味のある言葉でプログラムできる。マクロ文(#define)でも同様に記述できるが、マクロは1行で1つの値しか指定できないため、行が無駄に増える。
※enum型についてはC言語教科書176ページを参照
(f) のようにプログラムを修正し、正常に動くかどうかを確認する。
方向別の矩形は一定の規則によって並んでいるため、方向別の矩形を配列に持たなくても、方向の数値から計算で求めることができる。
例えば静止画像の矩形がMyChar.rectに、方向がMyChar.directionにそれぞれ格納されている場合、描画時に表示するべき矩形を求めれば、配列を持つことなく表示できる。【描画時に計算する例】
RECT rc;
rc.top = MyChar.top + MyChar.height * MyChar.direction;
rc.left = MyChar.left;
rc.bottom = rc.top + MyChar.height;
rc.right = MyChar.right;
hRet = g_pDDSBack->BltFast(MyChara.x, MyChara.y, g_pDDSChara,&rc, DDBLTFAST_SRCCOLORKEY);しかし、この方法は描画処理が行われるたびに計算を行わなければならないため、
CPUに負荷をかけてしまうことになる。ゲームループのたびに計算させていたのではスピードが落ちてしまう可能性があるので、ゲームループ中はなるべく余計な計算をさせないようなプログラミングを心がけるべきである。
ただし、配列を多く持たなければならないため、メモリは余計に消費してしまう。