DirectInput概要

■バージョン

	DirectX
		DirectX9

	VisualStudio
		VisualStudio 2017

■サンプル

以下のリンクでサンプルをダウンロードできます。

	サンプル

■使用方法

DirectInput機能を使用するためにいくつかの設定を行う必要があります。

●パス設定
	DirectXのincludeファイルやlibファイルがどこにあるかの設定が必要です。
	設定はメニューの「デバッグ」=>「プロジェクト名のプロパティ」で行います。
	
	directx_0033
	directx_0033

	プロパティウィンドウが表示されるので「構成プロパティ」=> 「VC++ディレクトリ」を
	クリックしてインクルードディレクトリとライブラリディレクトリに
	パス設定を行います。

	directx_0017
	directx_0017

	・共通
		パスの設定はまず設定したい項目をクリックすると「↓」アイコンが右端に
		表示されるのでそちらをクリックします。

		directx_0018
		directx_0018

		クリック後「編集」と書かれた項目が新しく表示されるので、クリックします。

		directx_0019
		directx_0019

		パス設定をするウィンドウが新たに表示ます。
		この画面の上部にある白い部分をダブルクリックすると右端に「…」が
		表示されるのでそちらをクリックします。

		directx_0020
		directx_0020

		参照先ディレクトリを決めるためのウィンドウが表示されるので、
		DirectXが置かれている場所まで移動してパスを設定します。

	・インクルードディレクトリ
		インクルードディレクトリではDirectXディレクトリの中にある
		Includeディレクトリを指定します。

		directx_0021
		directx_0021

	・ライブラリディレクトリ
		ライブラリディレクトリではDirectXディレクトリの中にある
		Libファイルの中の「x86」か「x64」いずれかを指定します。

		directx_0022
		directx_0022

		これはVisualStudio上段に「x86」または「x64」と書かれている項目があるので
		そちらを確認して書かれている方を指定してください。

		directx_0023
		directx_0023

●libファイルの設定
	パスの設定が完了したら次はlibファイルの設定を行います。
	方法はプロパティで指定する方法とコードで指定する方法があります。

	・プロパティ指定
		プロパティ指定はメニューの「デバッグ」=>「プロジェクト名のプロパティ」=>
		「リンカ」=>「入力」項目の「追加の依存ファイル」で行います。

		directx_0024
		directx_0024

		ここにDirectInputでは「dinput8.lib」と「dxguid.lib」を追加します。

		directx_0032
		directx_0032

	・コード指定
		コード指定の場合はファイルに以下のコードを追加します。	
			
			#pragma comment(lib, "dinput8.lib")
			#pragma comment(lib, "dxguid.lib")

■概要

DirectInputはDirectXの入力関連を扱うAPIです。
DirectInputで使用できる入力デバイスはキーボード、マウス、ゲームパッドなど、
様々なデバイスを扱うことができます。

※DirectInputは8でバージョンが止まっており、9以降では更新されていません。
  現在はXInputという後継のAPIがでています。

この記事はあくまでDirectInputの基本となっており、
ゲームの入力に関する基本はこちらに書いてます。

■DirectInputの構成

DirectInputは大きく二つのCOMインターフェースにより構成されています。
一つはDirectInputの大元となるIDirectInput8、
もう一つは入力情報を取得するための関数を持っているIDirectInputDevice8です。
※インターフェースがわからない方はインターフェースの部分をクラスに置き換えて下さい。
 DirectXを使用するだけならその認識で問題ないと思います。

directx_0034
directx_0034

●IDirectInput8
	上でも記述していますが、IDirectInput8はDirectInputを使用するにあたり、
	大元となるインターフェースです。
	IDirectInputDevice8を作成するための関数やDirectXを使用するPCが
	どこまで機能を使いこなせるかをチェックする関数を持ちます。

●IDirectInputDevice8
	IDirectInputDevice8はデバイス入力を管理するインターフェースです。
	このインターフェースに入力デバイスの取得関数があるので、
	ゲーム中の入力処理は主にこのインターフェースを使用することになります。

●DirectInputの初期化の流れ
	1.IDirectInput8インターフェイスの作成

	2.IDirectInputDevice8の作成

	3.デバイスのフォーマットの設定

	4.協調モードの設定

	5.デバイス制御開始

■IDirectInput8インターフェイスの作成

DirectInputを使用する場合、最初にIDirectInput8を作成します。
IDirectInput8はDirectX9におけるDirectInputの大元になりますが
この後に作成するIDirectInputDevice8を作成した後はほとんど使用しません。

●IDirectInput8取得関数:
	HRESULT DirectInput8Create(
			HINSTANCE,
			DWORD,
			REFIID,
			LPVOID *,
			LPUNKNOWN
			)

		内容:
			IDirectInput8の作成

		返り値:
			HRESULT
				S_OK => 成功
				E_FAILE => 失敗

		引数:
			HINSTANCE:
				アプリケーションのインスタンスハンドル

			DWORD:
				使用するDirectInputのバージョン
				(DIRECTINPUT_VERSION固定で問題なし)

			REFIID:
				目的とするインターフェースの識別子
				(IID_IDirectInput8固定で問題なし)

			LPVOID *:
				IDirectInput8インターフェースポインタ受け取り用

			LPUNKNOWN:
				基本的にNULL

●IDirectInput8取得関数使用例:
	HRESULT hr = DirectInput8Create(instance_handle, 
					DIRECTINPUT_VERSION,
					IID_IDirectInput8,
					(void**)&g_pInputInterface,
					NULL);

	if (FAILED(hr))
	{
		// 失敗
	}

■IDirectInputDevice8の作成

IDirectInput8を作成したあとはIDirectInputDevice8の作成をします。
IDirectInputDevice8はインプット関連のデバイス(キーボード、ゲームパッドなど)の
デバイスを管理しており、それらのデバイスの入力情報取得もこのデバイスから行います。

●IDirectInputDevice8作成関数
	HRESULT CreateDevice(
			REFGUID,
			LPDIRECTINPUTDEVICE*,
			LPUNKNOWN)

		内容:
			IDirectInputDevice8の作成

		返り値:
			HRESULT
				S_OK => 成功
				E_FAILE => 失敗

		引数:
			REFGUID:
				使用するデバイスの指定
				(キーボードならGUID_SysKeyBoardなど)

			LPDIRECTINPUTDEVICE*:
				IDirectInputDevice8インターフェース受け取り用

			LPUNKNOWN:
				基本的にNULL


●IDirectInputDevice8取得関数使用例:
	LPDIRECTINPUTDEVICE device;

	hr = g_pInputInterface->CreateDevice(
					GUID_SysKeyboard,
					&device,
					NULL);
	if (FAILED(hr))
	{
		// 失敗
	}

■デバイスのフォーマットの設定

入力デバイスを指定してIDirectInputDevice8を作成した後は、
その入力デバイスの設定を行います。
IDirectInputDevice8では使用する入力デバイスの指定は終わっていますが
設定は完了していません。
設定を完了しないと正常に入力情報の取得ができません。

●SetDataFormat関数
	HRESULT SetDataFormat(
			LPCDIDATAFORMAT)

		内容:
			指定したフォーマットを設定する

		返り値:
			HRESULT
				S_OK => 成功
				E_FAILE => 失敗

		引数:
			LPCDIDATAFORMAT:
				設定する入力デバイスのフォーマット
				デバイスはDirectInput側で用意されている
					キーボード => c_dfDIKeyboard
					マウス => c_dfDIMouse
					ジョイスティック => c_dfDIJoystick

●SetDataFormat関数使用例
	// デバイスのフォーマットの設定
	HRESULT hr = g_pKeyDevice->SetDataFormat(&c_dfDIKeyboard);
	if (FAILED(hr))
	{
		// 失敗
	}

■協調モードの設定

入力デバイスの設定が終わったら次は協調モードの設定を行います。
協調モードとは入力デバイスとシステム間の関係を設定することです。
ここでは「バックグラウンドモード or フォアグラウンドモード」と
「排他モード or 非排他モード」のいずれかを選択します。

●フォアグラウンドor バックグラウンド
	フォアグラウンドとバックグラウンドの違いは以下の通りです。

	・フォアグラウンド
		ウィンドウがバックグラウンドに移動したらデバイスの取得ができない

	・バックグラウンド
		ウィンドウがアクティブでない場合でもデバイスが取得できる

●排他 or 非排他
	排他的と非排他的の違いは以下の通りです。

	・排他的
		他のアプリケーションはその入力デバイスを取得できない。

	・非排他的
		他のアプリケーションでもそのまま入力デバイスの取得ができる

●SetCooperativeLevel関数詳細
	HRESULT SetCooperativeLevel(
			HWND,
			DWORD)

		内容:
			デバイスの協調モードの設定を行う

		返り値:
			HRESULT
				S_OK => 成功
				E_FAILE => 失敗

		引数:
			HWND:
				入力デバイスが関連付けられてる
				ウィンドウハンドル
				(入力を受けるウィンドウ)
			DWORD:
				協調レベルの設定フラグ
				(フォアとバック、排他と非排他は同時にする)

●SetCooperativeLevel関数使用例
	// 協調モードの設定
	HRESULT hr = g_pKeyDevice->SetCooperativeLevel(window_handle, 
					DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
	if (FAILED(hr))
	{
		// 失敗
	}

■デバイス制御開始

ここまでの処理ですべての必要な設定が完了したので最後にデバイスの入力制御を開始します。
この処理を実行することで、入力デバイスで入力された内容が取得できるようになります。

●Acquire関数詳細
	HRESULT Acquire()

		内容:
			入力デバイスの制御を開始する

		返り値:
			HRESULT
				S_OK => 成功
				E_FAILE => 失敗

		引数:
			なし

●Acquire関数使用例
	HRESULT hr = g_pKeyDevice->Acquire();

	if (FAILED(hr))
	{
		// 制御開始失敗
	}

■複数のデバイスを扱い方

今回の説明はキーボードのみでしたが、マウスやゲームパッドなどの入力デバイスも
同時に扱えるようにしたい場合は、別の入力デバイスに対して
「2.IDirectInputDevice8の作成」から再度行う必要があります。
また、入力デバイスによって、初期化の内容が異なることがあるので注意してください。
※別のデバイスについては別途記事を用意します。

■入力情報の取得

入力デバイスの入力情報は基本的に毎フレーム取得して、その情報を使用して
「入力された瞬間」「入力中」「入力終了」を判断します。
ただし、入力情報は入力デバイスのボタンを押されたか押されていないかしかわかりませんので、
「入力された瞬間」や「入力終了」は開発側で判定するようにしなければいけません。
※ゲームパッドではアナログスティックをどの程度倒しているかという情報の取得ができます。

●入力データ
	入力データは各デバイスによって変わるので、以下のように各デバイスごとに
	どのデバイスかがわかるようにデータの指定を行います。

	・キーボード
		c_dfDIKeyboradを指定した場合はバイト型の配列(要素数256)を用意

	・マウス
		c_dfDIMouseを指定した場合はDIMOUSESTATE構造体を用意

	・ゲームパッド
		c_dfDIJoystickを指定した場合はDIJOYSTATe構造体を用意

	入力データの取得が無事完了したら上記のデータ構造に情報が入ります。

●キーボード定数
	入力デバイスがキーボードの場合、どのキーが押されたかどうかはDirectInput側が
	定数を用意してくれているのでそちらを使用して判定します。

	・例
		右キー => DIK_RIGHT
		左キー => DIK_LEFET
		リターンキー => DIK_RETURN
		Aキー => DIK_A

●GetDeviceState関数詳細
	HRESULT GetDeviceState(
				DWORD
				LPVOID)

		内容:
			入力デバイスの情報を取得する

		返り値:
			HRESULT
				S_OK => 成功
				E_FAILE => 失敗

		引数:
			DWORD
				LPVOIDで指定しているデータのサイズ

			LPVOID
				デバイスの情報を受け取るためのデータのアドレス

●GetDeviceState関数使用例
	// キー情報取格納用
	BYTE KeyState[256];
	HRESULT hr;

	// キーボードデバイスのゲッター
	hr = g_pKeyDevice->GetDeviceState(256, KeyState);
	if (SUCCEEDED(hr))
	{	
		// 1フレーム前のキー情報の確保
		DWORD old = g_InputState.now;	

		// キー情報クリア
		g_InputState.now = CLEAR_KEY;	

		// 上キー
		if (KeyState[DIK_UP] & 0x80)
		{
			g_InputState.now |= UP_KEY; 
		}

		// 下キー
		if (KeyState[DIK_DOWN] & 0x80)
		{
			g_InputState.now |= DOWN_KEY; 
		}

		// 左キー
		if (KeyState[DIK_LEFT] & 0x80)
		{
			g_InputState.now |= LEFT_KEY; 
		}

		// 右キー
		if (KeyState[DIK_RIGHT] & 0x80)
		{
			g_InputState.now |= RIGHT_KEY; 
		}

		// リターンキー
		if (KeyState[DIK_RETURN] & 0x80)
		{
			g_InputState.now |= RETURN_KEY; 
		}

		// トリガー情報取得
		g_InputState.trg = (g_InputState.now & (~old));
		// 逆トリガー情報取得
		g_InputState.ntrg = (~g_InputState.now) & old;

	// デバイスロスト時は再度制御開始を呼ぶ
	} else if (hr == DIERR_INPUTLOST) {
		g_pKeyDevice->Acquire();
	}

	上のコードで各ボタンとの入力判定で0x80が使われているのは
	取得したデバイスでボタンが押されている場合は
	1バイトの最上位ビットが1となっているからです。

■解放

DirectInputも他のDirectXAPIと同様に終了時はインターフェース等を解放する必要があります。
解放は以下の順番で行います。
	
	①.デバイス制御の停止

	②.IDirectInputDevice8の解放

	③.IDirectInput8の解放

●デバイス制御の停止
	終了時にまず行うことは入力デバイスの制御を停止させることです。
	これは複数の入力デバイスを用意していた場合は全て停止させます。
	停止には「Unacquire関数」を使用します。

	・使用例
		g_pKeyDevice->Unacquire();


●IDirectInputDevice8の解放
	入力デバイスの制御が完了したらIDirectInputDevice8の解放を行います。
	この解放もデバイス制御の停止と同様に複数の入力デバイスを用意していた場合は
	全て解放します。
	解放は「Release関数」を使用します。

	・使用例
		g_pKeyDevice->Release();

●IDirectInput8の解放
	最後はDirectInputの大元であるIDirectInput8を解放します。
	解放は「Release関数」を使用します。
	g_pInputInterface->Release();