ゆとりーなの日記

日記的な事を書いて行くと思はれる

第9回〜キーボード入力〜

弾幕シューティングを作るんだから当然キー入力に応じて自機を動かすといったことをしなきゃいけなくなるわけです。というわけで今回はキーボード入力に関する内容です。
ゲームの入力にはDirectInputを使用することが多いみたいですが、これも例によってマイクロソフト先生により非推奨にされてしまったので先生が推奨するwin32APIとメッセージループを使うことにします。
win32APIである瞬間にキーが押されているかを取得するには、実はある関数を呼び出すだけで出来てしまうので、そいつをそのまま使ってやればいいでしょう。

// testapplication.cc
void TestApplication::update(DrawObject *draw_object) {
    assert(draw_object);
    if ((GetAsyncKeyState('Z') & 0x8000) || (GetAsyncKeyState(VK_DOWN) & 0x8000)) {
        draw_box->drawImage(0.f, 0.f, 640.f, 480.f, D3DXCOLOR(1.f, 1.f, 1.f, 1.f));
    }
}

ある瞬間にキーが押されているかを取得するしてくれる関数の正体は、GetAsyncKeyState関数です。上のコードでは、Zキーまたは↓キーが押された場合に画面いっぱいを真白にします。GetAsyncKeyState関数にはキーの種類を渡すことになるのですが、A〜Zキーを渡すには'A'〜'Z'、その他キーを渡すには仮想キーコード表なるものが存在するのでそいつを参照してください。大体VK_からはじまります。返り値はSHORTで、対象のキーが押されていた場合は最上位ビットが1になるので、押されているかを判定する場合は返り値に&0x8000して調べます。あと、前回のGetAsyncKeyState関数呼び出し時にも、対象キーが押されていた場合は最下位ビットが1になるのでこれも上手く使えると思います。
もうひとつ、メッセージループを使用したキー入力も実装しておきましょう。こちらはキーリピートの機能が何もせずに使えるので、メニュー画面の項目選択などに便利です。

// application.h
#pragma once
#include "deleter.h"

class GraphicDevice;
class DrawObject;
class SoundDevice;

class Application : private boost::noncopyable {
public:
    // インターフェイス
protected:
    virtual void keyDown(WPARAM key_code);
    // protectedインターフェイス
private:
    // 内部実装関係
};

void Application::keyDown(WPARAM key_code) {
}

// application.cc
LRESULT CALLBACK Application::subClassProcedure(HWND window_handle, UINT message, WPARAM wp, LPARAM lp, UINT_PTR this_ptr, DWORD_PTR) {
    Application * const application(reinterpret_cast<Application *>(this_ptr));
    assert(application);
    assert(application->window_handle_);
    switch (message) {
    case WM_KEYDOWN:
        application->keyDown(wp);
        break;
    case WM_DESTROY:
        RemoveWindowSubclass(application->window_handle_, subClassProcedure, this_ptr);
        PostQuitMessage(0);
        break;
    default:
        return DefSubclassProc(window_handle, message, wp, lp);
    }
    return 0;
}

メッセージループでのキー入力の取得も簡単で、プロシージャでWM_KEYDOWNメッセージを拾ってやります。この時wpには押されたキーの種類が入っています。WM_KEYDOWNメッセージを拾ったら呼び出されるApplication::keyDown仮想メソッドを用意して、継承先のクラスが自由にメッセージループを使ったキー取得が出来るようにしました。因みにキーを離したときはWM_KEYDOWNメッセージが飛んでくるのでこいつも有効に使ってやれると思います。

// testapplication.h
class TestApplication {
public:
    // インターフェイス
protected:
    virtual void keyDown(WPARAM key_code);
};

// testapplication.cc
void TestApplication::keyDown(WPARAM key_code) {
    if (key_code == 'Z') || (key_code == VK_DOWN) {
        // なんかする
    }
}

上記のコードはZキーまたは↓キーが押された場合になんかするコードです。key_codeとしてやってくるキーの種類はGetAsyncKeyState関数に渡すキーの種類と共通なので、A〜Zキーが押されたかを調べるときは'A'〜'Z'、その他のキーが押されたかを調べるときは仮想キーコード表の値と等しいかを調べることになります。
また、キーが離された瞬間もやるなら同じ様な感じになります。
以上でキーボードの入力はひとまず完成です。