カーソルを上下キーで移動し、STARTが選択されているときにスペースキーを押すとゲームがスタートするようなプログラムを考える。
カーソルがどの位置にいるかを示す変数があれば、その値によってどのメニューが選ばれているかがすぐに分かるだけでなく、表示する座標の計算にも使える。
Start.cppを次のように修正する。
//============================================================================= // Start処理関係の自作関数群 //============================================================================= #include "common.h" //----------------------------------------------------------------------------- // マクロ(Start処理でしか使わないもの) //----------------------------------------------------------------------------- #define MENUMAX 4 //----------------------------------------------------------------------------- // 構造体(Start処理でしか使わないもの) //----------------------------------------------------------------------------- typedef struct tarCURSTATUS { int x, y; RECT rect;int no;} CURSTATUS; ・ ・ ・
また、初期値として一番上(0)をセットする。
//----------------------------------------------------------------------------- // 関数名 : StartInit() // 機能概要: スタート処理初期化 //----------------------------------------------------------------------------- void StartInit(HWND hWnd) { ・ ・ ・ /* カーソル画像 */ SetRect(&Cursor.rect, 112, 586, 127, 603); Cursor.x = 180; Cursor.y = 300;Cursor.no = 0;//------------------------------------------------------- フレームナンバーセット g_FrameNo = START_FRAME; }
続いて、カーソルを移動させる処理を追加する。CursorMove関数を次のように修正する。END選択時にウィンドウを閉じるようにするため、
ウィンドウハンドルを受け取る必要がある(プロトタイプ宣言も修正すること)。
//----------------------------------------------------------------------------- // 関数名 : CursorMove() // 機能概要: カーソル移動処理 //----------------------------------------------------------------------------- static void CursorMove(HWND hWnd) {// カーソルキーを押したらカーソルが移動するif (KeyTbl[VK_UP] & 0x80 && 0 < Cursor.no)Cursor.no--;else if (KeyTbl[VK_DOWN] & 0x80 && Cursor.no < (MENUMAX - 1))Cursor.no++;// スペースキーが押されたらゲーム開始(START選択時のみ) if (KeyTbl[VK_SPACE] & 0x80) {switch (Cursor.no){case 0: // STARTg_FrameNo = GAME_INIT;return;case 1: // CONTINUE//g_FrameNo = CONTINUE_INIT;return;case 2: // OPTION//g_FrameNo = OPTION_INIT;return;case 3: // ENDPostMessage(hWnd, WM_CLOSE, 0, 0); //WM_DESTROYを誘発させるreturn;}} }カーソルキーの上を押すと、noから1を引き、下を押すと1を足す。選択肢は4つなので、0〜3の値しかとらないようにしてある。
CursorMove関数を実行する際、引数としてウィンドウ・ハンドルを渡すため、StartFrame関数を次のように修正する。
//----------------------------------------------------------------------------- // 関数名 : StartFrame() // 機能概要: 画面更新処理 //----------------------------------------------------------------------------- void StartFrame(HWND hWnd) { ScreenOut(); CursorMove(hWnd); }また、noの値を元にカーソルの表示位置を変えるため、カーソルの描画処理を次のように修正する。
//----------------------------------------------------------------------------- // 関数名 : ScreenOut() // 機能概要: 背景、タイトルなどを描画 //----------------------------------------------------------------------------- static void ScreenOut(void) { ・ ・ ・ /* カーソル */ hRet = g_pDDSBack->BltFast(Cursor.x,Cursor.y + mh * Cursor.no, g_pDDSStart, &Cursor.rect, DDBLTFAST_SRCCOLORKEY); if (hRet != DD_OK) return; }※mhはメニュー文字の表示間隔なので、カーソルの表示間隔としても使える。
確認
7-1(a) 〜 7-2(b) の修正を行い、カーソルが上下に移動するかどうかを確認する。
[ 実行結果サンプル ]
問題点
ゲームループが回るたびに入力キーのチェックを行っているため、連続押しになってしまい、カーソルが高速に移動してしまう。
これでは使い物にならない!!
カーソルの移動処理を修正し、一度移動したカーソルは一定時間経過するまで動かないようにする。これにより、カーソルがゆっくり動くようになる。
まず、カーソルの移動時間を格納する変数を、構造体に宣言する。
//----------------------------------------------------------------------------- // 構造体(Start処理でしか使わないもの) //----------------------------------------------------------------------------- typedef struct tarCURSTATUS { int x, y; RECT rect; int no;DWORD MoveTime; // 移動後経過時間} CURSTATUS;スタート画面表示後すぐにカーソルが移動できるように、MoveTimeに0をセットする。(nowTickCountをセットすると、スタート画面表示後一定時間が経たないとカーソルが移動しない)
//----------------------------------------------------------------------------- // 関数名 : StartInit() // 機能概要: スタート処理初期化 //----------------------------------------------------------------------------- void StartInit(HWND hWnd) { ・ ・ ・ /* カーソル画像 */ SetRect(&Cursor.rect, 112, 586, 127, 603); // Exit Cursor.x = 180; Cursor.y = 300; Cursor.no = 0;Cursor.MoveTime = 0;//------------------------------------------------------- フレームナンバーセット g_FrameNo = START_FRAME; }次に、カーソル移動処理を次のように修正する。
//----------------------------------------------------------------------------- // 関数名 : CursolMove() // 機能概要: カーソル移動処理 //----------------------------------------------------------------------------- static void CursorMove(void) {// 500ミリ秒ごとにカーソルを移動するif (nowTickCount - Cursor.MoveTime > 500){// カーソルキーを押したらカーソルが移動する if (KeyTbl[VK_UP] & 0x80 && 0 < Cursor.no){Cursor.no--;Cursor.MoveTime = nowTickCount;}else if (KeyTbl[VK_DOWN] & 0x80 && Cursor.no < (MENUMAX - 1)){Cursor.no++;Cursor.MoveTime = nowTickCount;}}// スペースキーが押されたらゲーム開始(START選択時のみ) if (KeyTbl[VK_SPACE] & 0x80) { switch (Cursor.no) { case 0: // START g_FrameNo = GAME_INIT; return; case 1: // CONTINUE // g_FrameNo = CONTINUE_INIT; return; case 2: // OPTION // g_FrameNo = OPTION_INIT; return; case 3: // END PostMessage(hWnd, WM_CLOSE, 0, 0); //WM_DESTROYを誘発させる return; } } }
- カーソル移動時間の初期値に0をセットしてあるため、スタート直後にカーソルを移動できる。
- カーソル移動時、カーソル移動時間に現在の経過時間をセットする。これにより、セット後は500ミリ秒を越えるまで移動処理が行われない。
- メニュー選択のスペースキーの判定は、カーソル移動とは別の場所で行う。これにより、500ミリ秒ごとという制約を受けない。
確認(必須問題)
7-2(c) の修正を行い、カーソルが500ミリ秒ごとに上下に移動するかどうかを確認する。
[ 実行結果サンプル ]
カーソルキーを離して再度押したら、500ミリ秒以内でもカーソルが移動するようにプログラムを改造する。(押しっぱなしだと、500ミリ秒ごとに移動)
[ 実行結果サンプル ]
注)このサンプルは、分かりやすいように1000ミリ秒ごとに移動するように作成してある。