DirectXプログラミング03 キーボード入力

̃Gg[͂ĂȃubN}[Nɒlj

今回はキーボード入力に対応させます。
前回のコードに手をいれ、矢印キーが押された時に画像を動かすように変更してみます。
やはり入力に反応があると面白みが出てきます。

WM_KEYDOWNメッセージを利用する

矢印キーに対応するには、WM_KEYDOWN メッセージのwParamが、
VK_LEFT、VK_RIGHT、VK_UP、VK_DOWNのときに、描画する座標をいじってあげればOKです。

/*	ウィンドウプロシージャ	*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	switch(iMsg){
		case WM_DESTROY :
			PostQuitMessage(0);
		break;
		case WM_KEYDOWN :
			switch((CHAR)wParam){
				case VK_ESCAPE:
				PostQuitMessage(0);
				break;
				case VK_LEFT:
					fPosX -= 4;
				break;
				case VK_RIGHT:
					fPosX += 4;
				break;
				case VK_UP:
					fPosY -= 4;
				break;
				case VK_DOWN:
					fPosY += 4;
				break;
			}
		break;
	}
	return DefWindowProc(hWnd, iMsg, wParam, lParam);
}

fPosX 、fPosYが描画する位置を表す変数です。
以下Drawメソッドのvec3Positionの指定に使っています。

/*	スプライトの描画	*/
void DrawSprite()
{
	pDevice->Clear(	0,
					NULL,
					D3DCLEAR_TARGET,
					D3DCOLOR_XRGB(100,100,100),
					1.0f,
					0);
	if(SUCCEEDED(pDevice->BeginScene())){
		RECT rect = {0, 0, 200, 149};
		D3DXVECTOR3 vec3Center(0,0,0);
		D3DXVECTOR3 vec3Position(fPosX,fPosY,0);
		pSprite->Begin(D3DXSPRITE_ALPHABLEND);
		pSprite->Draw( pTexture,
					   &rect,
					   &vec3Center,
					   &vec3Position,
					   0xFFFFFFFF);
		pSprite->End();
		pDevice->EndScene();
	}
	pDevice->Present(NULL,
					 NULL,
					 NULL,
					 NULL);
}

変更点はこれだけです。
しかし、これではDirectXを用いたキー入力対応プログラミングとは言えないですね。
(この方法でも速度を求められない状況ならば十分使えるらしいです。)

DirectInputを利用する

ということでDirectInputの初登場です。
まず以下のコードを追加します。

#include <dinput.h>
/*	DirectInput関連	*/
LPDIRECTINPUT8 pDinput = NULL;		/*	DirectInputオブジェクトのポインタ	*/
LPDIRECTINPUTDEVICE8 pKeyDevice = NULL;	/*	DirectInputデバイス・オブジェクトのポインタ	*/
LPD3DXFONT m_pFont;

インクルードと、必要な変数の宣言ですね。
〇〇オブジェクト、〇〇オブジェクト・デバイスのポインタを準備しておくのは、Direct3Dと同じですね。

次に、WinMain関数内で、Direct3Dの初期化関数を読んだ後に、DirectInputの初期化関数を呼ぶようにします。
(関数InitDinputの内容は後ほど。)

	/*	Direct3Dの初期化	*/
	if(FAILED(InitD3d(hWnd))){
		return 0;
	}
	/*	DirectInputの初期化	*/
	if(FAILED(InitDinput(hWnd))){
		return 0;
	}

メッセージループに入る前に初期化処理を実施するのはDirect3Dと同じ流れですね。

キーボード入力イベントのメッセージによる画像の移動はしないので、
該当部分を#if 0~#endifで削除しておきます。

/*	ウィンドウプロシージャ	*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	switch(iMsg){
		case WM_DESTROY :
			PostQuitMessage(0);
		break;
		case WM_KEYDOWN :
			switch((CHAR)wParam){
				case VK_ESCAPE:
				PostQuitMessage(0);
				break;
#if 0
				case VK_LEFT:
					fPosX -= 4;
				break;
				case VK_RIGHT:
					fPosX += 4;
				break;
				case VK_UP:
					fPosY -= 4;
				break;
				case VK_DOWN:
					fPosY += 4;
				break;
#endif
			}
		break;
	}
	return DefWindowProc(hWnd, iMsg, wParam, lParam);
}

DirectInput初期化関数の内容は以下のようになります。
ほぼ定型文っぽいです。

/*	DirectInputの初期化	*/
HRESULT InitDinput(HWND hWnd)
{
	HRESULT hr;
	/*	DirectInputオブジェクトの生成	*/
	if(FAILED(hr = DirectInput8Create(GetModuleHandle(NULL),
									  DIRECTINPUT_VERSION,
									  IID_IDirectInput8,
									  (VOID**)&pDinput,
									  NULL))){
		return hr;
	}
	/*	DirectInputデバイスオブジェクトの生成	*/
	if(FAILED(hr = pDinput->CreateDevice(GUID_SysKeyboard,
										 &pKeyDevice,
										 NULL))){
		return hr;
	}
	/*	デバイスをキーボードに設定	*/
	if(FAILED(hr = pKeyDevice->SetDataFormat( & c_dfDIKeyboard))){
		return hr;
	}
	/*	協調レベルの設定	*/
	if(FAILED(hr = pKeyDevice->SetCooperativeLevel(hWnd,
		DISCL_NONEXCLUSIVE | DISCL_BACKGROUND))){
		return hr;
	}
	/*	デバイスを取得する	*/
	pKeyDevice->Acquire();
	return S_OK;
}

■ DirectInput8Create関数
第四引数にDirectInputデバイスのポインタが格納されるようです。

HRESULT DirectInput8Create(
  HINSTANCE hinst,
  DWORD dwVersion,
  REFIID riidltf,
  LPVOID * ppvOut,
  LPUNKNOWN punkOuter
);

■CreateDevice
第二引数にDirectInputデバイス・オブジェクトのポインタが格納されるようです。
第一引数をGUID_SysKeyboardにすると、キーボード、
GUID_SysMouseにするとマウス用のデバイス・オブジェクトのポインタが格納されるらしいです。
■SetCooperativeLevel
デバイス(キーボード)をアプリケーションがどの程度占有するかという「協調レベル」を設定するらしい。
■Acquire
デバイスを取得(アクセス権を獲得)するらしい。
獲得に成功するとDI_OK、すでに取得中だとS_FALSEを返す。S_FALSEでも問題ないらしい。

最後に、押されているキーに対応して、描画座標を更新する処理です。

	/*	キーボードで押されているキーを調べ、対応する方向に移動させる	*/
	HRESULT hr = pKeyDevice->Acquire();
	if( (hr==DI_OK)||(hr==S_FALSE) ){
		BYTE diks[256];
		pKeyDevice->GetDeviceState(sizeof(diks), &diks);
		if(diks[DIK_LEFT] & 0x80){
			fPosX -= 4;
		}
		if(diks[DIK_RIGHT] & 0x80){
			fPosX += 4;
		}
		if(diks[DIK_UP] & 0x80){
			fPosY -= 4;
		}
		if(diks[DIK_DOWN] & 0x80){
			fPosY += 4;
		}
	}

■ GetDeviceState
キーボードの押下情報がBYTE型の配列に格納されるようです。
DIK_0が0キー
DIK_AがAキーを表す要素らしいです。
キーが押されていると、押下情報の上位1bitがたつので&0x80で判定しているわけですね。

WM_KEYDOWNメッセージを利用した場合と、DirectInputを利用した場合では、
後者の方が処理よりも動きがスムーズに思えました(気のせい?)

以下に全体コードを載せます。
プロジェクトのプロパティ->構成プロパティ->リンカ->入力->追加の依存ファイル
の設定に以下を追加しておかないと、リンカエラー(~ は未解決です。)で怒られるので注意です。
dinput8.lib
dxguid.lib

#include <windows.h>
#include <d3dx9.h>
#include <dinput.h>

#define SAFE_RELEASE(p) {if(p){(p)->Release(); (p)=NULL;}}

/*	Direct3D関連	*/
LPDIRECT3D9 pD3d;
LPDIRECT3DDEVICE9 pDevice;
LPDIRECT3DTEXTURE9 pTexture;
LPD3DXSPRITE pSprite;
/*	DirectInput関連	*/
LPDIRECTINPUT8 pDinput = NULL;
LPDIRECTINPUTDEVICE8 pKeyDevice = NULL;
LPD3DXFONT m_pFont;

FLOAT fPosX=200, fPosY=150;

/*	プロトタイプ宣言	*/
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HRESULT InitD3d(HWND);
HRESULT	InitDinput(HWND);
VOID AppProcess();
#if 0
VOID DrawSprite();
#endif
VOID FreeDx();

/*	エントリ関数	*/
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR szStr, INT iCmdShow)
{
	HWND hWnd = NULL;
	MSG msg;
	
	/*	ウィンドウの初期化	*/
	LPCWSTR szAppName = L"DirectXプログラミング03 キー入力 DirectInput利用";
	WNDCLASSEX wndclass;
	wndclass.cbSize			= sizeof(wndclass);
	wndclass.style			= CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc	= WndProc;
	wndclass.cbClsExtra		= 0;
	wndclass.cbWndExtra		= 0;
	wndclass.hInstance		= hInst;
	wndclass.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	wndclass.lpszMenuName	= NULL;
	wndclass.lpszClassName	= szAppName;
	wndclass.hIconSm		= LoadIcon(NULL, IDI_APPLICATION);
	RegisterClassEx(&wndclass);
	hWnd = CreateWindow(szAppName,
						szAppName,
						WS_OVERLAPPEDWINDOW,
						0,
						0,
						640,
						480,
						NULL,
						NULL,
						hInst,
						NULL);
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
	
	/*	Direct3Dの初期化	*/
	if(FAILED(InitD3d(hWnd))){
		return 0;
	}
	/*	DirectInputの初期化	*/
	if(FAILED(InitDinput(hWnd))){
		return 0;
	}
	/*	メッセージループ	*/
	ZeroMemory(&msg, sizeof(msg));
	while(msg.message != WM_QUIT){
		if(PeekMessage( &msg,
						NULL,
						0U,
						0U,
						PM_REMOVE)){
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		} else {
			AppProcess();
#if 0
			DrawSprite();
#endif
		}
	}
	/*	メッセージループから抜けたらオブジェクトをすべて開放する。	*/
	FreeDx();

	/*	アプリケーションを終了する	*/
	return(INT)msg.wParam;
}

/*	ウィンドウプロシージャ	*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
	switch(iMsg){
		case WM_DESTROY :
			PostQuitMessage(0);
		break;
		case WM_KEYDOWN :
			switch((CHAR)wParam){
				case VK_ESCAPE:
				PostQuitMessage(0);
				break;
#if 0
				case VK_LEFT:
					fPosX -= 4;
				break;
				case VK_RIGHT:
					fPosX += 4;
				break;
				case VK_UP:
					fPosY -= 4;
				break;
				case VK_DOWN:
					fPosY += 4;
				break;
#endif
			}
		break;
	}
	return DefWindowProc(hWnd, iMsg, wParam, lParam);
}

/*	Direct3Dの初期化	*/
HRESULT InitD3d(HWND hWnd)
{
	/*	Direct3Dオブジェクトの生成	*/
	if(NULL == (pD3d = Direct3DCreate9(D3D_SDK_VERSION))){
		MessageBox(0, L"Direct3Dの作成に失敗しました", L"", MB_OK);
		return E_FAIL;
	}
	/*	Direct3Dデバイスオブジェクトの生成	*/
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.BackBufferFormat	= D3DFMT_UNKNOWN;
	d3dpp.BackBufferCount	= 1;
	d3dpp.SwapEffect		= D3DSWAPEFFECT_DISCARD;
	d3dpp.Windowed			= TRUE;
	if(FAILED(pD3d->CreateDevice(	D3DADAPTER_DEFAULT,
									D3DDEVTYPE_HAL,
									hWnd,
									D3DCREATE_MIXED_VERTEXPROCESSING,
									&d3dpp,
									&pDevice))){
		MessageBox(0, L"HALモードでDirect3Dデバイスを作成できませんnREFモードで再試行します", NULL, MB_OK);
		if(FAILED(pD3d->CreateDevice(D3DADAPTER_DEFAULT,
									 D3DDEVTYPE_REF,
									 hWnd,
									 D3DCREATE_MIXED_VERTEXPROCESSING,
									 &d3dpp,
									 &pDevice))){
			 MessageBox(0, L"DIRECT3Dデバイスの作成に失敗しました", NULL, MB_OK);
			 return E_FAIL;
		}
	}
	/*	テクスチャオブジェクトの生成	*/
	if(FAILED(D3DXCreateTextureFromFileEx(pDevice,
										  L"Sprite.jpg",
										  200,
										  149,
										  0,
										  0,
										  D3DFMT_UNKNOWN,
										  D3DPOOL_DEFAULT,
										  D3DX_FILTER_NONE,
										  D3DX_DEFAULT,
										  0xFF000000,
										  NULL,
										  NULL,
										  &pTexture))){
		MessageBox(0, L"テクスチャの作成に失敗しました", L"", MB_OK);
		return E_FAIL;
	}
	/*	スプライトオブジェクトの作成	*/
	if(FAILED(D3DXCreateSprite(	pDevice,
								&pSprite
								))){
		MessageBox(0, L"スプライトの作成に失敗しました", L"", MB_OK);
		return E_FAIL;
	}
	return S_OK;
}
/*	DirectInputの初期化	*/
HRESULT InitDinput(HWND hWnd)
{
	HRESULT hr;
	/*	DirectInputオブジェクトの生成	*/
	if(FAILED(hr = DirectInput8Create(GetModuleHandle(NULL),
									  DIRECTINPUT_VERSION,
									  IID_IDirectInput8,
									  (VOID**)&pDinput,
									  NULL))){
		return hr;
	}
	/*	DirectInputデバイスオブジェクトの生成	*/
	if(FAILED(hr = pDinput->CreateDevice(GUID_SysKeyboard,
										 &pKeyDevice,
										 NULL))){
		return hr;
	}
	/*	デバイスをキーボードに設定	*/
	if(FAILED(hr = pKeyDevice->SetDataFormat( & c_dfDIKeyboard))){
		return hr;
	}
	/*	協調レベルの設定	*/
	if(FAILED(hr = pKeyDevice->SetCooperativeLevel(hWnd,
		DISCL_NONEXCLUSIVE | DISCL_BACKGROUND))){
		return hr;
	}
	/*	デバイスを取得する	*/
	pKeyDevice->Acquire();
	return S_OK;
}
/*	アプリケーション処理関数	*/
VOID AppProcess()
{
	/*	スプライトの描画	*/
	pDevice->Clear(	0,
					NULL,
					D3DCLEAR_TARGET,
					D3DCOLOR_XRGB(100,100,100),
					1.0f,
					0);
	if(SUCCEEDED(pDevice->BeginScene())){
		RECT rect = {0, 0, 200, 149};
		D3DXVECTOR3 vec3Center(0,0,0);
		D3DXVECTOR3 vec3Position(fPosX,fPosY,0);
		pSprite->Begin(D3DXSPRITE_ALPHABLEND);
		pSprite->Draw( pTexture,
					   &rect,
					   &vec3Center,
					   &vec3Position,
					   0xFFFFFFFF);
		pSprite->End();
		pDevice->EndScene();
	}
	/*	キーボードで押されているキーを調べ、対応する方向に移動させる	*/
	HRESULT hr = pKeyDevice->Acquire();
	if( (hr==DI_OK)||(hr==S_FALSE) ){
		BYTE diks[256];
		pKeyDevice->GetDeviceState(sizeof(diks), &diks);
		if(diks[DIK_LEFT] & 0x80){
			fPosX -= 4;
		}
		if(diks[DIK_RIGHT] & 0x80){
			fPosX += 4;
		}
		if(diks[DIK_UP] & 0x80){
			fPosY -= 4;
		}
		if(diks[DIK_DOWN] & 0x80){
			fPosY += 4;
		}
	}
	pDevice->Present(NULL,
					 NULL,
					 NULL,
					 NULL);
}
#if 0
/*	スプライトの描画	*/
void DrawSprite()
{
	pDevice->Clear(	0,
					NULL,
					D3DCLEAR_TARGET,
					D3DCOLOR_XRGB(100,100,100),
					1.0f,
					0);
	if(SUCCEEDED(pDevice->BeginScene())){
		RECT rect = {0, 0, 200, 149};
		D3DXVECTOR3 vec3Center(0,0,0);
		D3DXVECTOR3 vec3Position(fPosX,fPosY,0);
		pSprite->Begin(D3DXSPRITE_ALPHABLEND);
		pSprite->Draw( pTexture,
					   &rect,
					   &vec3Center,
					   &vec3Position,
					   0xFFFFFFFF);
		pSprite->End();
		pDevice->EndScene();
	}
	pDevice->Present(NULL,
					 NULL,
					 NULL,
					 NULL);
}
#endif
VOID FreeDx()
{
	SAFE_RELEASE(pKeyDevice);
	SAFE_RELEASE(pDinput);
	SAFE_RELEASE(pTexture);
	SAFE_RELEASE(pSprite);
	SAFE_RELEASE(pDevice);
	SAFE_RELEASE(pD3d);
}

このシリーズ、これまでDirect3Dを利用して描画していたけど、表示は2D画像のみでした。
次回はいよいよ3Dの表示に挑戦します。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です