はかせのラボ

私の頭の中を書いていく雑記ブログです

DirectX 入力

今日も今日とてDirectX勉強回です。
今回はDirectXでの入力DirectInputを触っていこうと思います。

キーボード操作でカメラをぐりぐり回して
前回作った色付きキューブを眺めてニヤニヤするのが目的ですw

※もちろん入力を取得してプログラムに
 反映できるようにすることが本質的な目的です

DirectInputとは

DirectInputとはDirectXで入力を扱うAPIのことです。
普通の.NETのアプリならば、
Console.ReadLineやGetAsyncKeyStateで入力を取ると思います。

ですがこれらでは取得するためにCPUでのオーバーヘッドが発生し、
リアルタイムにユーザーの入力を取ることは難しいらしいです。

DIrectInputでは直接デバイスドライバから入力を検知することで、
入力のラグを限りなく0に近づけることができるそうです。

そんな優秀そうなDIrectInputなんですが、
現在更新は8の段階でほぼ止まっているようです。
キーボードやマウスの入力なんてそんな頻繁に変わらないでしょうし
それで更新してないのかもしれないですね。

ただDirectX11でも問題なく動くということなので
そのまま使っていこうと思います。

使うための準備

まずは使うために必要なincludeファイルやライブラリです。

#include "dinput.h"
#include "dinputex.h"
#pragma comment(lib , "dinput8.lib")
#pragma comment(lib, "dxguid.lib")

dinput.hはDirectInputを扱うために必要なヘッダファイルです。
ここに入力を取るために使う定数やらなんやらが定義されているみたいです。

dinputex.hはネットから拾ってきたヘッダファイルです。
これを使わないと初期化等で使用する定数とかが
未解決ですみたいなエラーを吐きました。

私の環境設定が悪いのかもしれませんが
このファイルをincludeして解決したので、
大きな問題が起きない限りこのままでいこうと思います。

dinput8.libはDirectInputを使うためのライブラリ、
dxguid.libは後で使うGUIDを使うために必要なライブラリです。

実際に使う

まずは初期化です。

//初期化関数
HRESULT InitDirectInput(HWND hwnd)
{
	HINSTANCE hinst = (HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE);
	HRESULT hr = DirectInput8Create(hinst,DIRECTINPUT_VERSION,IID_IDirectInput8,(void**)&mInput,NULL);
	if (FAILED(hr)) return S_FALSE;
	hr = mInput->CreateDevice(GUID_SysKeyboard, &mInputDevice, NULL);
	if (FAILED(hr)) 
	{
		ExitDirectInput();
		return S_FALSE;
	}
	hr = mInputDevice->SetDataFormat(&c_dfDIKeyboard);
	if (FAILED(hr))
	{
		ExitDirectInput();
		return S_FALSE;
	}
	//入力の取り方 前面にいるときのみ取得し入力を独占しない
	hr = mInputDevice->SetCooperativeLevel(hwnd,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	if (FAILED(hr))
	{
		ExitDirectInput();
		return S_FALSE;
	}
	//入力受付
	mInputDevice->Acquire();
	return S_OK;
}

基本的にはDirectX11を始めたときにやった初期化と同じ流れで
初期化に使う関数に必要なパラメータを設定して
関数を呼んであげるだけです。

次に入力の取得です。

//現在の入力状態を保持する
void SetInputState()
{
	mInputDevice->GetDeviceState(sizeof(mInputBuffer), (LPVOID)&mInputBuffer);
}
//指定したキーの入力状況を確認する
BOOL GetInputState(int key)
{
	if (mInputBuffer[key] & isInputNum) return TRUE;
	else return FALSE;
}

毎フレーム全てのキーの入力状態を保持し、
入力情報が欲しいところがGetInputKeyに
確認したいキーの定数を引数として渡すと、
入力されていればTRUE、されていなければFALSEを返します。

実際の使用例です。

//入力取得
SetInputState();
if (GetInputState(DIK_UP)) x += 0.001f;
if (GetInputState(DIK_DOWN)) x -= 0.001f;
if (GetInputState(DIK_RIGHT)) y -= 0.001f;
if (GetInputState(DIK_LEFT)) y += 0.001f;
if (GetInputState(DIK_ESCAPE)) return FALSE;

上下キーの入力でx軸、
左右キーの入力でy軸を中心にカメラを回しています。
Escapeキーを押すとゲームループを終了しゲームを終了させます。

最後に終了処理です。

//終了関数
void ExitDirectInput()
{
	//入力拒否
	mInputDevice->Unacquire();
	if (mInput) mInput->Release();
	if (mInputDevice)mInputDevice->Release();
}

確保したメモリリソースを解放しています。
今までと違うのは入力を受け付けないようにしてから
解放処理をしていることです。

こうしないと終了処理のタイミングで何かしら入力があったとき
使おうとしているリソースを解放してしまって
どういう挙動になるかわからないからだと思っています。

次回予告

これで
・ポリゴンを使って物を表示する。
・入力を受けて動作する。
以上のゲームを作るうえで
必要最低限の動作はできるようになったと思います。
一段落ついたところで次はコードを書き直したいと思います。

正直今のままではオブジェクト増やしたり、
それぞれに独自の処理を持たせたりしようとすると
ゲームループにベタっと貼りつけるしかないので少々辛いですw

こういうのってモチベが一定以上あるときにやらないと
なぁなぁで終わってしまうことが多い気がするので
一段落つきましたしモチベがあるうちにやってしまおうと思います。

今苦労しとけば後で本格的にゲームを作るときに楽になりますからねw

次は書き直し終わった時に
どういう風に直したみたいな話をしたいなと思います。