第7章 キー操作を制御する

7−2 メニューを選択させるには?(カーソルの移動)

カーソルを上下キーで移動し、STARTが選択されているときにスペースキーを押すとゲームがスタートするようなプログラムを考える。

(a) カーソルの位置を格納する変数を用意

カーソルがどの位置にいるかを示す変数があれば、その値によってどのメニューが選ばれているかがすぐに分かるだけでなく、表示する座標の計算にも使える。
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;

}

(b) カーソルを移動させる処理を追加する

続いて、カーソルを移動させる処理を追加する。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: // 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;
        }
    }

}

カーソルキーの上を押すと、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) の修正を行い、カーソルが上下に移動するかどうかを確認する。

[ 実行結果サンプル ]

問題点

ゲームループが回るたびに入力キーのチェックを行っているため、連続押しになってしまい、カーソルが高速に移動してしまう。
これでは使い物にならない!!

(c) カーソルの移動にウェイトをかける

カーソルの移動処理を修正し、一度移動したカーソルは一定時間経過するまで動かないようにする。これにより、カーソルがゆっくり動くようになる。

まず、カーソルの移動時間を格納する変数を、構造体に宣言する。

//-----------------------------------------------------------------------------
// 構造体(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;
        }
    }

}

確認(必須問題)

7-2(c) の修正を行い、カーソルが500ミリ秒ごとに上下に移動するかどうかを確認する。

[ 実行結果サンプル ]

応用(自由問題)

カーソルキーを離して再度押したら、500ミリ秒以内でもカーソルが移動するようにプログラムを改造する。(押しっぱなしだと、500ミリ秒ごとに移動)

[ 実行結果サンプル ]

注)このサンプルは、分かりやすいように1000ミリ秒ごとに移動するように作成してある。


[ TOP ]