クラスまみれのゲームプログラミング入門

26:キーボード入力を受け付ける

 キャラクターを移動させることまではできるようになりましたので、次はプレイヤー自らがキャラクターを動かせるようにしましょう。キーボード入力の受け付けはDirectInputで行います。

DirectInputの宣言をする

 入力関連のデータを格納するCGameObjectの派生クラス「CInput」のヘッダファイルに以下の定義を追加します。これでDirectXが提供する最新バージョンのDirectInputが利用できるようになります。

 DirectInputを利用できるようにするには、GUIDと呼ばれる定義も必要になります。これらの定義は「dxguid.lib」に格納されているので、コンパイル時にこのライブラリをリンクするようにしておきます。

Input0.h
#pragma once

#include "GameObject.h"

//利用するDirectInputバージョンの宣言
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

//DirectInputに関連するライブラリのリンクを宣言
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")


コンストラクタ・デストラクタの追加

 ゲーム全体でDirectInputを初期化するのはまず一度きりなので、初期化と解放処理はコンストラクタ・デストラクタで行うことにします。DirectInputを管理するオブジェクトはLPDIRECTINPUT8(IDirectInput8)、入力機器(ここではキーボード)の管理を行うオブジェクトはLPDIRECTINPUTDEVICE8(IDirectInputDevice8)となっています。

Input1.h
#pragma once

#include "GameObject.h"

#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")

class CInput : public CGameObject
{
private:
	LPDIRECTINPUT8 pInput;

	LPDIRECTINPUTDEVICE8 pKeyDevice;
};

Input1.cpp
#include "Input.h"

CInput::CInput()
{
	pInput = NULL;
	pKeyDevice = NULL;
}

CInput::~CInput()
{
	RELEASE(pKeyDevice);
	RELEASE(pInput);
}


キーボードデバイスを取得

 次にDirectInputの作成および入力機器情報の作成を行います。DirectInput自体の作成はDirectInput8Createで、入力機器情報の作成はIDirectInput8::CreateDevice()にて行います。CreateDeviceの第1引数には対象のデバイスIDを指定する必要があります。ここでは、キーボードデバイスであるGUID_SysKeyboardを指定します。

Input2.cpp
void CInput::Init()
{
	if(pInput == NULL){
		// インターフェイスの取得
		HRESULT hr;
		hr = DirectInput8Create(
			GetHInstance(),			// ソフトのインスタンスハンドル
			DIRECTINPUT_VERSION,	// DirectInputのバージョン
			IID_IDirectInput8,		// 取得するインターフェイスのタイプ
			(LPVOID*)&pInput,		// インターフェイスの格納先
			NULL					// COM集成の制御オブジェクト(使わないのでNULL)
			);

		if(FAILED(hr)){
			DXTRACE_MSG(_T("DirectInputの初期化に失敗しました"));
			return;
		}

		hr = pInput->CreateDevice(
			GUID_SysKeyboard,	// 受け付ける入力デバイス
			&pKeyDevice,		// IDirectInputDevice8格納先
			NULL				// COM集成の制御オブジェクト(使わないのでNULL)
			);

		if(FAILED(hr)){
			DXTRACE_MSG(_T("DirectInputDeviceの初期化に失敗しました"));
			return;
		}
	}
}

 デバイス情報の作成が成功したら、次はどのキーが押されたかを取得するためのデータを指定する必要があります。これは、ジョイパッドやキーボードなどでは取得できる情報のサイズに違いが出てくるために必要な処理です。キーボードの場合は、256個のBYTE配列に結果が格納されますので、このデータの宣言もヘッダにて行っておきましょう。

 つづいて協調モードの指定を行います。これは、データを取得する状況を指定するもので、3つの状態を組み合わせることができます。指定はIDirectInputDevice8::SetCooperativeLevel()で行います。

①取得するタイミング
DISCL_BACKGROUNDウィンドウがアクティブになっていなくても常に取得
DISCL_FOREGROUNDウィンドウがアクティブになっているときのみ取得
②取得データの提供先
DISCL_EXCLUSIVEDirectInputを呼び出したアプリのみに限定
DISCL_NONEXCLUSIVEすべてのアプリケーションに提供
③Windowsキーのロック
DISCL_NOWINKEYウィンドウズキーをロックして無効にする

Input3.h
/* 省略 */

class CInput : public CGameObject
{
private:
	LPDIRECTINPUT8 pInput;

	LPDIRECTINPUTDEVICE8 pKeyDevice;
	BYTE keydata[256];

protected:
	void Init();
};

Input3.cpp
void CInput::Init()
{
	if(pInput == NULL){
		/* 省略 */
		
		// データフォーマットの設定
		pKeyDevice->SetDataFormat(&c_dfDIKeyboard);

		// 協調モードの設定
		pKeyDevice->SetCooperativeLevel(GetHWnd(),
			DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);

		// キーバッファの初期化
		ZeroMemory(keydata, 256);
	}
}


hpのデスクトップPC お買い得キャンペーン情報!!


入力された情報を取得

 キーボードからどのキーが押されたかは、IDirectInputDevice8::Acquire()によって入力の受け付けを開始した後で、IDirectInputDevice8::GetDeviceState()によってリアルタイム取得できます。データは256バイトの配列に丸ごと格納されるので、その中から特定のキーが押されているかどうかを取得する関数を別に用意しておくと便利でしょう。

Input4.h
/* 省略 */

class CInput : public CGameObject
{
private:
	LPDIRECTINPUT8 pInput;

	LPDIRECTINPUTDEVICE8 pKeyDevice;
	BYTE keydata[256];

public:
	BOOL IsKeyDown(int key);

protected:
	void Init();
	void Exex();
};

Input4.cpp
void CInput::Exec()
{
	if(pKeyDevice){
		// 入力の受け付け開始
		pKeyDevice->Acquire();

		// データを取得(256は取得するデータのサイズ)
		pKeyDevice->GetDeviceState(256, keydata);
	}
}

BOOL CInput::IsKeyDown(int key)
{
	if(pKeyDevice == NULL) return FALSE;
	
	// 最上位1ビットに「押されているか」どうかのデータが格納されていることに注意
	// 最下位1ビットには「トグル(オン・オフの状態)」が格納されている
	return (keydata[key] & 0x80) ? TRUE : FALSE;
}