キーボードの操作を受け付けない

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ゴンマサ

キーボードの操作を受け付けない

#1

投稿記事 by ゴンマサ » 9年前

お世話になっております。今回も宜しくお願い致します。
以下のコードはウインドウの中に画像を描画し、それを動かすという内容です。
環境はWindows7、VisualC++Express、DirectX9になります。


226行目から、キーボードからの直接データを取得して画像を右に動かす関数を記述をしているのですが、
キーボードを押しても反応がありません。

228行目の画像を移動させる記述は有効なため、問題はキーボードの情報を取得できていないということだと思うのですが、
何が問題なのか見当がつきません。

いくつかのサイトを調べたところ、下記のようにキーボードの状態の取得をキーの取得の前に記述するという方法を見つけ実践してみましたが、いずれもエラーが起きてしまいました。

コード:

//画像の移動//これでプレイヤー画像を動かす
HRESULT idou()
{

    //テストその1
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
	HRESULT hr;
    hr = g_pDIDevice->GetDeviceState(256,diKeyState);
	if(SUCCEEDED(hr))
	{
		
	}
	else if(hr==DIERR_INPUTLOST)
	g_pDIDevice->Acquire();
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////

	//m_vec2.x += 1.0f;//この記述は有効

    
    //テストその2
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
	if( (hr==DI_OK)||(hr==S_FALSE) )//エラーが起きる
	
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
		if(diKeyState[DIK_G] & 0x80)//Gボタンを押したら画像が右に動く
		{
			m_vec2.x += 1.0f;
		}
	}
	return S_OK;
}


ここからが本コードになります。
何か知恵をお借りできれば幸いです。
宜しくお願い致します。


コード:

#define NAME "DirectX勉強中"
#define INITGUID
#include <Windows.h>
#include <d3dx9.h>

#define DIRECTINPUT_VERSION     0x0800          // DirectInputのバージョン指定
#include <dinput.h>//ダイレクトインプット(バージョンを指定してからインプット)
// ライブラリのリンク
#pragma once
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")
#pragma comment(lib,"dinput8.lib")
#pragma comment(lib,"dxguid.lib")
#define SAFE_RELEASE(x) if(x)  { x->Release(); x=NULL; }// #define & 構造体の定義
 
//アプリケーション情報
HINSTANCE hInstApp;							//アプリケーションのインスタンスハンドル
HWND                    g_hWnd = NULL;      // Window Handle
LPDIRECT3D9             g_pD3D = NULL;     //インターフェイスの取得のためのポインタの作成(構造体)
LPDIRECT3DDEVICE9       g_pDEV = NULL;      // 描写用デバイス(インターフェイス)
								//デバイス(装置、外部周辺機器)関連を管理するインターフェイス

LPDIRECTINPUTDEVICE8 g_pInput = NULL;//DirectInput8インターフェイスの取得のためのポインタの作成
						
#define DIDEVICE_BUFFERSIZE	100						// デバイスに設定するバッファ・サイズ

 
LPDIRECT3DTEXTURE9 pTexture;//背景のテクスチャハンドル
LPD3DXSPRITE pSprite;//スプライトハンドル
D3DXMATRIX m_Matrix;//行列
D3DXVECTOR2 m_vec2;//位置(x座標,y座標)

D3DDISPLAYMODE          d3ddm;//画面モード
D3DPRESENT_PARAMETERS   d3dpp;//インターフェイスの取得のためのポインタの作成

BYTE diKeyState[256];//キーボード・デバイス定数の状態を設定

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////
//DirectInputに関する記述(DirectInputの初期化を指す)


LPDIRECTINPUT8 g_pDInput = NULL;					// DirectInput
LPDIRECTINPUTDEVICE8 g_pDIDevice = NULL;			// DirectInputデバイス
//DirectInputの初期化
bool InitDInput()
{
	HRESULT hr;

	//1.DirectInput8インターフェイスの取得
	hr = DirectInput8Create(hInstApp,//アプリケーションのインスタンスハンドル
						   DIRECTINPUT_VERSION,
						   IID_IDirectInput8,
						   (void**)g_pInput,
						   NULL);
	if(FAILED(hr)){return false;}



	//2.キーボードデバイスでの設定
	hr = g_pDInput->CreateDevice(GUID_SysKeyboard, &g_pDIDevice,NULL);
	if(FAILED(hr)){return false;}

	//3.データ形式を設定ーーキーボードデバイス用
	hr = g_pDIDevice->SetDataFormat(&c_dfDIKeyboard);
	if(FAILED(hr)){return false;}

	//キーボードの状態を取得
	hr = g_pDIDevice->GetDeviceState(sizeof(diKeyState),&diKeyState);
	//hr = g_pDIDevice->GetDeviceState(256,diKeyState);
	if(SUCCEEDED(hr))
	{

	}
	else if(hr==DIERR_INPUTLOST)
		g_pDIDevice->Acquire();
		

	//4.協調モードの設定
	hr = g_pDIDevice->SetCooperativeLevel(g_hWnd,//ウインドウハンドル
										  DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
		if(FAILED(hr)){return false;}

	//5.軸モードの設定(この下に相対値モードがあるが今回は絶対値を採用)
	//絶対値モード
	DIPROPDWORD diprop;
	diprop.diph.dwSize      = sizeof(diprop);
	diprop.diph.dwHeaderSize = sizeof(diprop.diph);
	diprop.diph.dwObj        = 0;
	diprop.diph.dwHow        = DIPH_DEVICE;
	
	hr = g_pDIDevice->SetProperty(DIPROP_AXISMODE,
								  &diprop.diph);
	if(FAILED(hr)){return false;}

	//6.バッファサイズの設定
	diprop.dwData = DIDEVICE_BUFFERSIZE;
	hr = g_pDIDevice->SetProperty(DIPROP_BUFFERSIZE, &diprop.diph);
	if(FAILED(hr)){return false;}

	//7. 入力制御開始
	g_pDIDevice->Acquire();

	return true;
}


////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

HRESULT textureCreate()//テクスチャの作成
{
    HRESULT hr;//ハンドル
    hr = D3DXCreateTextureFromFile(g_pDEV,"player.png",&pTexture);
    if(FAILED(hr))
        return false;
        return S_OK;
}
 
//スプライトの作成
HRESULT spriteCreate()
{
    HRESULT hr;//ハンドル
    hr = D3DXCreateSprite(g_pDEV,&pSprite);//1.使用するデバイス、2.受け取る変数
    if(FAILED(hr))
    return false;
    return S_OK;
}

//スプライトの描画
HRESULT drawsprite()
{
    HRESULT hr;//ハンドル

	//変換行列を作成
	//D3DXMatrixTransformation2D(&m_Matrix,NULL,0,NULL,NULL,0,&D3DXVECTOR2(0,200));
	D3DXMatrixTransformation2D(&m_Matrix,NULL,0,NULL,NULL,0,&m_vec2);//座標の指定を変更

    //塗りつぶし
    g_pDEV->Clear(0, NULL, (D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER),
				  D3DCOLOR_RGBA(100,100,255,0), 1.0f, 0);
    g_pDEV->BeginScene();//スプライトシーンの開始
		pSprite->Begin(D3DXSPRITE_ALPHABLEND|D3DXSPRITE_DONOTSAVESTATE);
 

		RECT SrcRect = {0,0,32,32};    //テクスチャ内の使用範囲
		D3DXVECTOR3 Center(64,49,0);    //スプライト内の中心
		D3DXVECTOR3 Position(64,64,0);//スプライトを表示する位置、ウインドウの左上が0,0//320,240

		hr = pSprite->SetTransform(&m_Matrix);
		hr = pSprite->Draw(pTexture,//pSrcTexture,
							NULL,
							NULL,
							NULL,
							0xffffffff);

		if(FAILED(hr))
		return false;
		pSprite->End();//スプライトを描画して終了

    g_pDEV->EndScene();//シーンを終了
    return g_pDEV->Present(NULL, NULL, NULL, NULL);
    return S_OK;
}
 
//初期化処理
HRESULT Init3DDev()
{
 
    //構造体の設定
 
    //インターフェイスの取得
    if (NULL==(g_pD3D=Direct3DCreate9(D3D_SDK_VERSION)))    return E_FAIL;
 
    if (FAILED(g_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddm)))   return E_FAIL;
 
    //D3DPRESENT_PARAMETERS構造体の設定
    ZeroMemory(&d3dpp,sizeof(d3dpp));
    d3dpp.Windowed                  = TRUE;        // ウィンドウモード
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;      // 映像信号に同期してフリップする
    d3dpp.BackBufferFormat          = d3ddm.Format;// カラーモードの指定
    d3dpp.EnableAutoDepthStencil    = TRUE;        // デプスバッファ(Zバッファ)とステンシルバッファを作成
    d3dpp.AutoDepthStencilFormat    = D3DFMT_D16;  // デプスバッファとして16bitを使う
    
 
    //デバイスの作成:CreateDeviceの実行
    if (FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,g_hWnd,
                                    D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&g_pDEV)))
 
    return E_FAIL;
    return S_OK;
}
 
//ビューポートの作成
HRESULT Viewport()
{
    HRESULT hr;//ハンドル
 
    //ビューポートの作成
    D3DVIEWPORT9 vp;
    vp.X = 0;
    vp.Y = 0;
    vp.Width = d3dpp.BackBufferWidth;
    vp.Height = d3dpp.BackBufferHeight;
    vp.MinZ = 0.0f;
    vp.MaxZ = 1.0f;
    hr = g_pDEV -> SetViewport(&vp);
 
    if(FAILED(hr))
    return false;
    return S_OK;
 
}
 
// DirectX Graphics の終了処理
void Cleanup(void)
{
    SAFE_RELEASE(g_pDEV);
    SAFE_RELEASE(g_pD3D);
}
 



//画像の移動
HRESULT idou()
{
	//m_vec2.x += 1.0f;//この記述は有効

		if(diKeyState[DIK_G] & 0x80)//Gボタンを押したら画像が右に動く
		{
			m_vec2.x += 1.0f;
		}

	return S_OK;
}



//全てのまとめ(描画、移動など)
void Action()
{
	idou();
	drawsprite();
}

// ウィンドウ処理(自分で処理したときは 0 を返す)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam)
{
    switch(msg)
    {   case WM_DESTROY:
            Cleanup();
            PostQuitMessage(0);
            break;
        case WM_KEYDOWN:
            switch(wParam)
            {   case VK_ESCAPE:
                    DestroyWindow(hWnd);
                    break;
            }
            break;
        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}
 
//★ WinMain()
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR lpCmdLine, int nCmdShow)
{   
    MSG         msg;
 
    WNDCLASSEX wc = { sizeof(WNDCLASSEX),CS_CLASSDC,WndProc,0,0, 
                      hInst,NULL,NULL,NULL,NULL,NAME,NULL
                    };
    if (!RegisterClassEx(&wc))  return FALSE;
 
    g_hWnd= CreateWindowEx(0,NAME,NAME,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
                           CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInst,NULL);
 
    if (!g_hWnd)  return FALSE;
 
    // DirectX Graphics の初期化(画像関連の初期化であり、キーボード入力、サウンドは別に作る)
    if (FAILED(Init3DDev()))    return FALSE;

	//DirectInputの初期化
	InitDInput();


    // ウインドウ表示
    ShowWindow(g_hWnd, SW_SHOWNORMAL);
    UpdateWindow(g_hWnd);
    // メッセージ・ループ
    ZeroMemory(&msg,sizeof(msg));
 
    textureCreate();//テクスチャの作成
    spriteCreate();//スプライトの作成
    Viewport();//ビューポートの作成
 
	//画像の初期位置のセット
	m_vec2.x = 0.0f;
	m_vec2.y = 200.0f;

    //ループ
    while(msg.message!=WM_QUIT)
    {  
        //プロシージャ
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
        {   TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
       else{	 
			Action();
	   }
		//elseを消すとウインドウ終了時にエラーが起きる
    }
    UnregisterClass(NAME, hInst);
    return  TRUE;
}


ゴンマサ

Re: キーボードの操作を受け付けない

#2

投稿記事 by ゴンマサ » 9年前

閲覧いただきありがとうございます。

本コード248行目のウインドウプロシージャを以下のように変更したところ、画像を動かすことは出来ました。


コード:

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam)
{
    switch(msg)
    {   case WM_DESTROY:
            Cleanup();
            PostQuitMessage(0);
            break;
        case WM_KEYDOWN:
            switch(wParam)
            {   case VK_ESCAPE:
                    DestroyWindow(hWnd);
                    break;

			case 'G':
				m_vec2.x += 5.0f;
				break;

            }
            break;
        default:
            return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}



ですがこの記述だとボタンを押した時に少し動き、一旦止まってから連続して動き出し始めるようになってしまい、
スムーズな動きになりません。


またidou関数内で、キーボードの状態を取得しようとすると、5行目の diKeyStateでアクセス違反が起きてしまいます。

コード:

    void  idou()
    {   BYTE    diKeyState[256];
        HRESULT hr;

        hr= g_pDIDevice->GetDeviceState(256,diKeyState);
        if (SUCCEEDED(hr))
        {   
       if(diKeyState[DIK_G]&0x22)//Gボタンを押したら画像が右に動く
       {	
           m_vec2.x += 1.0f;
       }

        }
	else if(hr==DIERR_INPUTLOST)
	g_pDIDevice->Acquire();
    }
多くのサイトではキーボードの状態の取得を上記のように説明されているようなのですが、
それは便宜上であって、DirectInputの初期化と同時に行うべきなのでしょうか。

何かお気づきの点がありましたら宜しくお願いします。

djann
記事: 27
登録日時: 12年前

Re: キーボードの操作を受け付けない

#3

投稿記事 by djann » 9年前

ゴンマサ さんが書きました:いくつかのサイトを調べたところ、下記のようにキーボードの状態の取得をキーの取得の前に記述するという方法を見つけ実践してみましたが、いずれもエラーが起きてしまいました。
上記以下に書かれたコード(g_pDIDevice->GetDeviceState(256,diKeyState);)のように、デバイスの状態を取得することは必須です。大抵はゲームループ中のどこかで一回取得し、入力情報が必要な場合はdiKeyStateを参照する、貴方が書いたHRESULT idou()関数のようになるでしょう。


今回のコードの問題点はいくつかあります。

1. g_pDIDeviceは果たして本当に初期化されていますでしょうか?
  少なくとも私の環境でビルドしたところ、g_pDIDeviceは正しく初期化されていませんでした。
  まず、DirectInput8Create()が失敗しているのですが、その原因として
  hInstAppとやらは、どこでその値が入れられているのでしょう?
  また、DirectInput8Create()の第4引数に渡すのはLPDIRECTINPUT8ではなくLPDIRECTINPUT8*です。それをvoid**にキャストして渡してください。
  違いが分からなければ、とりあえず&を付けましょう。そして調べて理解してみましょう。

2. 1でちゃんとLPDIRECTINPUT8*を渡してDirectInput8Create()が成功しても、g_DIDeviceを初期化する為のg_pDInputは初期化されていません。
  なぜなら、現在のコードではDirectInput8Create()にはg_pInputを渡しています。
  g_pDInputを使用したくても、初期化されていないので使用できません。

3. 2でちゃんとg_pDInputが初期化され、その後のコードでg_pDIDeviceが設定されたとしても、GetDeviceState()は失敗します。
  なぜなら、デバイスへのアクセス権を取得していないからです。Acquire()を呼び出しましょう。
  その後デバイスがロストした際等には、再取得のためにDIERR_INPUTLOSTが返ってこなくなるまでAcquire()を呼び出し続けるといった形になります。
  または少し行儀が悪いかもしれませんが、ゲームループに一回のGetDeviceState()の直前に毎回Acquire()を呼ぶような形にしてもいいのではないかと思います。

本質的な問題と、直接的な回答は以上になります。


他に気になった点ですが、まずは質問として

・DIRECTINPUT_VERSIONを手打ちで定義しているのはなぜですか?使用している環境のヘッダバージョンとは違う、特定のバージョンを指定したいと言った要望があったのでしょうか?
・INITGUIDを定義しているのはなぜですか?何か定義を行おうとしていたのでしょうか?

次に問題点として

・LPDIRECTINPUTDEVICE8が二つ定義されています。また、コメントの内容が間違っています。

コード:

LPDIRECTINPUTDEVICE8 g_pInput = NULL;//DirectInput8インターフェイスの取得のためのポインタの作成
  こちら、DirectInput8取得の為ではありません。元のコードで間違ってDirectInput8Create()に投げ込んではいますが・・・。
  使用しない変数は、使用しないうちにむやみに定義するのはやめましょう。

・NAMEをソースの一番上で定義していますが、AやWを使い分けない(意味が分からないなら置いといてください)のであれば、せめてWindows.hのインクルードより下で定義し、かつ文字列リテラルをTEXT()マクロ等で囲いましょう。

・どの開発環境かは存じませんが、アクセス違反を起こした際には「変数の値が正しいか?」を見てみましょう。
  今回で言えばg_pDIDevice->GetDeviceState()でエラーが起きた。というのが分かるはずです。
  ではそこで使用している各種変数の値が、果たして本当に正しいか(正しそうか)を見ましょう。
  するとnullptrなので、じゃあ初期化はちゃんと行われているのか?を今度は見てください(そこまで到達しているか否かを何らかの方法(ブレークポイントや、またはprintfでもいい)でみましょう。
  到達しているのであればその結果を、到達していないのであればどこで到達しないことになったのかを見ていきます。
  そうすれば、実際に問題になる場所を徐々に特定していけるはずです。

・そもそも、マウスやキーボードの入力にDirectInputを使う事を、Microsoftは非推奨だと言っています。
  はい、そうなのです。現代ではMicrosoftは、マウスやキーボードの入力を取得する際、DirectInputは使わずに、Windowsのメッセージループ(つまりはウィンドウのプロシージャ)を用いるよう推奨しています。
  DirectInputを無理やり使うより簡単に書けますし、そちらの方が私もおすすめだと思います。


ゴンマサ さんが書きました:本コード248行目のウインドウプロシージャを以下のように変更したところ、画像を動かすことは出来ました。
ですがこの記述だとボタンを押した時に少し動き、一旦止まってから連続して動き出し始めるようになってしまい、
スムーズな動きになりません。
  
どうせならこうしてください。

コード:

LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
	switch ( msg )
	{
	case WM_DESTROY:
		Cleanup();
		PostQuitMessage( 0 );
		break;
	case WM_KEYDOWN:
		switch ( wParam )
		{
		case VK_ESCAPE:
			DestroyWindow( hWnd );
			break;
		default:
			diKeyState[ wParam ] = 1;
		}
		break;
	case WM_KEYUP:
		diKeyState[ wParam ] = 0;
		break;
	default:
		return DefWindowProc( hWnd, msg, wParam, lParam );
	}
	return 0;
}

HRESULT idou()
{
	if ( diKeyState[ 'G' ] ){
		m_vec2.x += 1.0f;
	}
}
DirectInputによってdiKeyStateを書き換えてもらう代わりに、自分でWindowsメッセージループを用いてdiKeyStateに値を設定する例です。



そして余談ですが

コード:

//ループ
while ( msg.message != WM_QUIT )
{
	//プロシージャ
	if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
	{
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	} else{
		Action();
	}
	//elseを消すとウインドウ終了時にエラーが起きる
}
とありますが、なぜこの書き方だとelseが必要になるのか分かりますか?
ループを抜けるのはWM_QUITが来た時になりますが、ウィンドウプロシージャのWM_DESTROYのところでCleanup()を行っていますよね?
WM_QUITが来るのは、間違いなくWM_DESTROYが来た後(PostQuitMessage()を呼び出した後にもう一度以上PeekMessage()が呼ばれた時)になります。elseが無いと、WM_DESTROYのメッセージが処理された後に、Action()に入ってしまいます。
そうすると、Cleanup()が行われて既にnullptrになってしまっているg_pDEV等にアクセスしてしまい、アクセス違反のエラーが起きてしまいます。

なぜか駄目だからelseを書く。ではなく、原因を特定した上で納得して最適な形のコードを書いていきましょう。

※これだけ長文書いたので、この文章中に何個間違いがあるかが楽しみです()

ゴンマサ

Re: キーボードの操作を受け付けない

#4

投稿記事 by ゴンマサ » 9年前

djann 様
返信拝見させていただきました。返事が遅れ申し訳ありません。
とても詳しく回答いただき恐縮です。本当にありがとうございます。

最初に、私が今回書いたコードは参考書をベースにして、わからない部分をネットのサイトなどを参考に書き足していたため、継ぎはぎだらけのコードになってしまいました。

「NAMEをソースの一番上で定義」などはその参考にしたコードをそのまま使用したものです。
INITGUIDについても同様であり、何とかしてエラーが起きないようにすることを優先するあまり、
関数の意味に気を配っていませんでした。

・DIRECTINPUT_VERSIONの手打ちについては、添削のミスです。
このコード内では必要ないものでした。申し訳ありません。

この記述を必要とした理由は、最初はLPDIRECTINPUTDEVICEを記述したのですがエラーが起こり、
いくつかのサイトを見てLPDIRECTINPUTDEVICE8と記述して、バージョンを指定したところ、
エラーが起きなくなりました。最終的に何故この記述をしなくてもエラーが起きなくなったのかは
自分自身わかっていません。


・hInstAppは指定していないのですが、何故か私の開発環境ではビルドが通り、デバッグも問題なく
ウインドウと画像が表示されます。これから調べていこうと思います。

DirectInputの非推奨、またプロシージャに関してもご教授いただきありがとうございます。
おかげで、スムーズに画像を移動させることができました。
なのでこのトピックは解決とさせていただきます。
早くゲームを作りたいと思うあまり細かい部分について深く理解しようとする姿勢が足りなかったと思います。
もう少しじっくりと細部を考えるように心がけたいと思います。

閲覧いただいた皆様もありがとうございました。
また何かありましたら、宜しくお願い致します。

閉鎖

“C言語何でも質問掲示板” へ戻る