キー入力について

DXライブラリには特定キーの入力状態を得るCheckHitKey(int KeyCode)という関数が存在する。 以下は、矢印キーによって画像を動かし、Zキーを1回押す度に画像を変えるサンプルプログラムである。

#include <DxLib.h>

int WINAPI WinMain( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR lpCmdLine , int nCmdShow )
{
    ChangeWindowMode( TRUE );//非全画面にセット
    SetGraphMode( 800 , 600 , 32 );//画面サイズ指定
    SetOutApplicationLogValidFlag( FALSE ) ;//Log.txtを生成しないように設定
    if(DxLib_Init() == 1){return -1;}//初期化に失敗時にエラーを吐かせて終了

    //
    //ここで敵やプレイヤーのオブジェクトの実体を作る
    //
    int graphic[2];
    graphic[0]  = LoadGraph("char01.png");
    graphic[1]  = LoadGraph("char02.png");

    int x = 400, y = 300, pattern = 1;

    while( ProcessMessage()==0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
    {
        ClearDrawScreen();//裏画面消す
        SetDrawScreen( DX_SCREEN_BACK ) ;//描画先を裏画面に

     //上下左右
        if(CheckHitKey(KEY_INPUT_UP) == 1)
     {
         y--;
     }
     if(CheckHitKey(KEY_INPUT_DOWN) == 1)
     {
         y++;
     }
     if(CheckHitKey(KEY_INPUT_LEFT) == 1)
     {
         x--;
     }
     if(CheckHitKey(KEY_INPUT_RIGHT) == 1)
     {
         x++;
     }
     if(CheckHitKey(KEY_INPUT_Z) == 1)
     {
         pattern++;
     }

     DrawRotaGraph(x, y, 1.0, 0, graphic[pattern % 2], TRUE);

        ScreenFlip();//裏画面を表画面にコピー
    }

    DxLib_End();
    return 0;
}

何かおかしいと思った方もいるのではないだろうか。そう、CheckHitKeyにはある問題点が存在する。
というのも、この関数は特定キーが入力されているか否かを確認するのみであるため、そのキーを押している長さを得ることができない。
そのため以下のような事態が起こりうる。

  • ボタンを1回だけ押した時に行いたい処理が、ボタンを押している間永続的に行われる。

上記はZキーを1回押す度に画像を変更するプログラムであったはずである。
だが、CheckHitKeyをそのまま使っているために、ボタンを押してすぐに離さないと画像が2回以上変わる仕様になってしまっている。

  • ボタンを押した長さを取得できないため、溜め撃ちのようなボタンを一定時間以上押すことを要する処理ができない。

前述した通り、CheckHitKeyにはボタンを押している長さを得る機能は無い。そのため、このままだと溜め撃ちのような処理はできない。

こうした問題点は、以下のような関数を自作することによって解決する。

//キー取得用の配列
char buf[256] = {0};
int keystate[256] = {0};

//キー入力状態を更新する関数
void keyupdate()
{
    GetHitKeyStateAll(buf);
    for(int i = 0; i< 256; i++)
    {
        if(buf[i] == 1) keystate[i]++;
        else keystate[i] = 0;
    }
}

Key()は特定のボタンが押されている間、対応するint型の変数の数値を増やし続けるという関数である。
この関数を用いることによって、ボタンを押した時の長さが取得可能になり、前述した問題点が解決された。

#include <DxLib.h>

//キー取得用の配列
char buf[256] = { 0 };
int keystate[256] = { 0 };

//キー入力状態を更新する関数
void keyupdate()
{
    GetHitKeyStateAll(buf);
    for (int i = 0; i< 256; i++)
    {
        if (buf[i] == 1) keystate[i]++;
        else keystate[i] = 0;
    }
}

int WINAPI WinMain( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR lpCmdLine , int nCmdShow )
{
    ChangeWindowMode( TRUE );//非全画面にセット
    SetGraphMode( 800 , 600 , 32 );//画面サイズ指定
    SetOutApplicationLogValidFlag( FALSE ) ;//Log.txtを生成しないように設定
    if(DxLib_Init() == 1){return -1;}//初期化に失敗時にエラーを吐かせて終了

    //
    //ここで敵やプレイヤーのオブジェクトの実体を作る
    //
    int graphic[2];
    graphic[0]  = LoadGraph("char01.png");
    graphic[1]  = LoadGraph("char02.png");

    int x = 400, y = 300, pattern = 1;

    while( ProcessMessage()==0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
    {
        keyupdate();

        ClearDrawScreen();//裏画面消す
        SetDrawScreen( DX_SCREEN_BACK ) ;//描画先を裏画面に

        //上下左右
        if(keystate[KEY_INPUT_UP] >= 1)
        {
            y--;
        }
        if (keystate[KEY_INPUT_DOWN] >= 1)
        {
            y++;
        }
        if (keystate[KEY_INPUT_LEFT] >= 1)
        {
            x--;
        }
        if (keystate[KEY_INPUT_RIGHT] >= 1)
        {
            x++;
        }
        if (keystate[KEY_INPUT_Z] == 1)
        {
            pattern++;
        }

        DrawRotaGraph(x, y, 1.0, 0, graphic[pattern % 2], TRUE);

        ScreenFlip();//裏画面を表画面にコピー
    }

    DxLib_End();
    return 0;
}

1度だけボタンを押した際に実行したい処理は
keystate[KEY_INPUT_Z] == 1 ボタンを押している間実行し続けたい処理は
keystate[KEY_INPUT_Z] >= 1 を使う。 10フレームに一度実行したい処理の場合は
keystate[KEY_INPUT_Z] % 10 == 1 と書けばよいだろう。