Direct Input で図形を動かす

DirectX の LPDIRECTINPUT8 でキーをセンスして図形を上下左右に動かします。
DirectInput は「マウス」「キーボード」「ジョイスティック」などの入力機器を統一的に管理します。
入力装置からの入力は、普通イベントとしてウインドウ・プロシージャに通知されます。
この方法だと、イベント・キューにメッセージが溜まってしまうとリアルタイムに処理が出来なくなり、ゲームプログラムに向かない場合があります。
DirectInput を使うと、入力装置の状態をリアルタイムに取得できます。

DirectInput のプログラムは結構面倒で、単にマウスのボタンやキーボードをセンスするだけならば GetAsyncKeyState() 関数を使う方法がお勧めです。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. #include と #define と #pragma です。
    プライベートライブラリを使わないで作成しています。
    DirectInput を使うときは dinput.h を取り込んで、dinput8.lib と dxguid.lib をリンクして下さい。
    Direct X9 でも DIRECTINPUT8 が使われています。
    g_pDInput が DirectInput の Object で、g_pDIDev がキーボードのデバイスです。
    g_bActive はウインドウのアクティブ状態を示すフラグです。
    Pos, WPos は図形を描画する座標と直前に描画した座標です。
        /*****************************************/
        /*★ Direct Input  keyboard    前田 稔 ★*/
        /*****************************************/
        #define     NAME    "DirectX8 Input"
        #include    <windows.h>
        #include    <dinput.h>
        #pragma     comment(lib,"dinput8.lib")
        #pragma     comment(lib,"dxguid.lib")
    
        #define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p)=NULL; } }
    
        //DirectInput Object
        LPDIRECTINPUT8        g_pDInput  = NULL;    // DirectInput
        LPDIRECTINPUTDEVICE8  g_pDIDev   = NULL;    // DirectInput デバイス
    
        //Global Area
        HWND                  g_hWnd;
        HINSTANCE             g_Inst;
        bool                  g_bActive = FALSE;    // ウインドウのアクティブ状態
        POINT                 Pos,WPos;
        
  2. WinMain() ではウインドウを生成して DirectInput の初期化を行います。
    メッセージループの KeyCheck() がキーをセンスする関数です。
    Draw() が Pos の座標に図形を描画する関数です。
    ループから抜け出すと Cleanup() で領域を開放してプログラムを終了します。
        //★ Windows Main
        int WINAPI  WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int)
        {   MSG     msg;
    
            g_Inst= hInst;
            WNDCLASSEX wc = { sizeof(WNDCLASSEX),CS_CLASSDC,MainWndProc,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);
    
            // DirectInputに関する初期化
            if (FAILED(InitDInput()))   {  Cleanup();  return FALSE;  }
            Pos.x= 100; Pos.y= 50;
    
            ShowWindow(g_hWnd,SW_SHOWDEFAULT);
            UpdateWindow(g_hWnd);
            ZeroMemory(&msg,sizeof(msg));
            while(msg.message!=WM_QUIT)
            {   if (PeekMessage(&msg,NULL,0U,0U,PM_REMOVE))
                {   TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else
                {   //Direct Input でキーのチェック
                    if (g_pDIDev)   KeyCheck();
                    Draw();
                }
            }
            Cleanup();
            UnregisterClass(NAME,wc.hInstance);
            return 0;
        }
        
  3. DirectInput の初期化を行う関数です。
    g_pDInput に DirectInput の Object を作成します。
    g_pDIDev にキーボードのデバイスを作成します。
    キーボードのデータ・フォーマットを設定します。
    キーボードの協調モードを設定します。
    Acquire() で入力の制御を開始します。
        // DirectInput 初期化
        HRESULT  InitDInput(void)
        {   HRESULT hr;
    
            // DirectInputの作成
            hr = DirectInput8Create(g_Inst,DIRECTINPUT_VERSION,IID_IDirectInput8,(void**)&g_pDInput,NULL);
            if (FAILED(hr)) 
            {   MessageBox(NULL,"DirectInput8オブジェクトの作成に失敗","Direct Input Error",MB_OK);
                return hr;
            }
            // デバイス・オブジェクトを作成
            hr = g_pDInput->CreateDevice(GUID_SysKeyboard, &g_pDIDev, NULL); 
            if (FAILED(hr))
            {   MessageBox(NULL,"DirectInputDevice8オブジェクトのの作成に失敗","Direct Input Error",MB_OK);
                return hr;
            }
            // データ・フォーマットを設定
            hr = g_pDIDev->SetDataFormat(&c_dfDIKeyboard);
            if (FAILED(hr))
            {   MessageBox(NULL,"c_dfDIMouse2形式の設定に失敗","Direct Input Error",MB_OK);
                return hr;
            }
            // 協調モードを設定(フォアグラウンド&非排他モード)
            hr = g_pDIDev->SetCooperativeLevel(g_hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
            if (FAILED(hr))
            {   MessageBox(NULL,"フォアグラウンド&非排他モードの設定に失敗","Direct Input Error",MB_OK);
                return hr;
            }
            // 入力制御開始
            g_pDIDev->Acquire();
            return S_OK;
        }
        
  4. キーを調べる KeyCheck() 関数です。
    ESC キーでプログラムを終了します。
    4,6,8,2 のテンキーと上下左右の矢印キーで図形を動かします。
        // キー入力
        void  KeyCheck()
        {   BYTE    KeyState[256];
            HRESULT hr;
    
            hr= g_pDIDev->GetDeviceState(256,KeyState);
            if (SUCCEEDED(hr))
            {   if (KeyState[DIK_ESCAPE]&0xf0)  PostMessage(g_hWnd,WM_CLOSE,0,0);   //ESC
                if (KeyState[DIK_NUMPAD4]&0xf0) Pos.x--;    //テンキーの 4
                if (KeyState[DIK_LEFT]&0xf0)    Pos.x--;    //テンキーの ←
                if (KeyState[DIK_NUMPAD6]&0xf0) Pos.x++;    //テンキーの 6
                if (KeyState[DIK_RIGHT]&0xf0)   Pos.x++;    //テンキーの →
                if (KeyState[DIK_NUMPAD8]&0xf0) Pos.y--;    //テンキーの 8
                if (KeyState[DIK_UP]&0xf0)      Pos.y--;    //テンキーの ↑
                if (KeyState[DIK_NUMPAD2]&0xf0) Pos.y++;    //テンキーの 2
                if (KeyState[DIK_DOWN]&0xf0)    Pos.y++;    //テンキーの ↓
            }
            else if (g_bActive && hr==DIERR_INPUTLOST)  g_pDIDev->Acquire();
        }
        
  5. Pos の座標に図形(円)を描画する関数です。
    前回描画した座標(WPos)と同じ時はそのままリターンします。
    ウインドウを黒でクリアして円を描画します。
    プログラムをシンプルにするためにバックバッファを使わないで、そのまま描画しているので画面がチラツキます。
        // 描画処理
        void  Draw(void)
        {   HDC     hdc;
            RECT    rect;
    
            if (Pos.x==WPos.x && Pos.y==WPos.y) return;
            GetWindowRect(g_hWnd,&rect);
            rect.right-= rect.left;
            rect.bottom-= rect.top;
            rect.left=rect.top= 0;
            hdc= GetDC(g_hWnd);
            FillRect(hdc,&rect,(HBRUSH)GetStockObject(BLACK_BRUSH));
            Ellipse(hdc,Pos.x,Pos.y,Pos.x+50,Pos.y+50);
            ReleaseDC(g_hWnd,hdc);
            WPos= Pos;
        }
        
  6. プログラムを終了する前に、入力制御を終了して領域を開放して下さい。
        // 終了の処理
        void  Cleanup()
        {   if (g_pDIDev)    g_pDIDev->Unacquire(); 
            SAFE_RELEASE(g_pDIDev);
            SAFE_RELEASE(g_pDInput);
        }
        
  7. CALLBACK 関数です。
    ウインドウがアクティブになったときに g_bActive を設定して、入力の制御を開始します。
        // CALLBACK 関数
        LRESULT CALLBACK  MainWndProc(HWND hWnd,UINT msg,UINT wParam,LONG lParam)
        {
            switch(msg)
            {   case WM_ACTIVATE:
                    if (g_pDIDev==NULL)     break;
                    g_bActive = wParam!=WA_INACTIVE;
                    if(wParam==WA_INACTIVE) g_pDIDev->Unacquire();
                    else                    g_pDIDev->Acquire();
                    return 0L;
                case WM_DESTROY:
                    PostQuitMessage(0);
                    break;
            }
            return DefWindowProc(hWnd,msg,wParam,lParam);
        }
        

超初心者の方のために全ソースコードを掲載します。 (^_^;)
全ソースコード

前田稔(Maeda Minoru)の超初心者のプログラム入門

超初心者のプログラム入門(DirectX9 game program)

超初心者のプログラム入門(DirectX10 game program)