2コントローラを使う

DirectX で、二個のコントローラ(Joystick)を使って、円と矩形を別々に動かします。
マウスやキーボードと違い、デバイスを列挙して操作するデバイスを選択しなければなりません。

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

プログラムの説明

  1. #include と #define と #pragma です。
    DirectInput を使うときは dinput.h を取り込んで、dinput8.lib と dxguid.lib をリンクして下さい。
    Direct X9 でも DIRECTINPUT8 が使われています。
    g_pDInput が DirectInput の Object で、g_pDIDev と g_pDIDev2 がコントローラのデバイスです。
    g_bActive はウインドウのアクティブ状態を示すフラグです。
    Pos, WPos, Pos2, WPos2 は図形を描画する座標と直前に描画した座標です。
    g_diDevCaps は列挙したデバイスからジョイスティックの能力を調べる領域です。
        /*******************************************/
        /*★ Direct Input  Controller    前田 稔 ★*/
        /*******************************************/
        #define     NAME    "DirectX8 Input Controller2"
        #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 デバイス-1
        LPDIRECTINPUTDEVICE8  g_pDIDev2 = NULL;     // DirectInput デバイス-2
    
        //Global Area
        HWND                  g_hWnd;
        HINSTANCE             g_Inst;
        bool                  g_bActive = FALSE;    // ウインドウのアクティブ状態
        POINT                 Pos,WPos;             // 円の座標
        POINT                 Pos2,WPos2;           // 矩形の座標
        DIDEVCAPS             g_diDevCaps;          // ジョイスティックの能力
        
  2. WinMain() ではウインドウを生成して DirectInput の初期化を行います。
    WinMain() 関数はキーボードの時と同じです。
  3. DirectInput の初期化を行う関数です。
    g_pDInput に DirectInput の Object を作成します。
    コントローラのデバイスを列挙して使用するデバイスを選択して g_pDIDev と g_pDIDev2 に設定します。
    列挙したデバイスは EnumJoysticksCallback() に渡されます。
    コントローラのデータ・フォーマット(g_pDIDev, g_pDIDev2)を設定します。
    コントローラの協調モード(g_pDIDev, g_pDIDev2)を設定します。
    コールバック関数を使って各軸のモード(g_pDIDev, g_pDIDev2)を設定します。
    Acquire() で入力の制御(g_pDIDev, g_pDIDev2)を開始します。
    解かり易いように、それぞれを2回繰り返しましたが、一個にまとめてソースコードを整理して下さい。
        // 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->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback,
                                        NULL, DIEDFL_ATTACHEDONLY);
            if (FAILED(hr) || g_pDIDev==NULL || g_pDIDev2==NULL)
            {   MessageBox(NULL,"DirectInputDevice8オブジェクトの作成に失敗","Direct Input Error",MB_OK);
                return hr;
            }
            // データ形式を設定
            hr = g_pDIDev->SetDataFormat(&c_dfDIJoystick2);
            if (FAILED(hr))
            {   MessageBox(NULL,"c_dfDIJoystick2形式の設定に失敗","Direct Input Error",MB_OK);
                return hr;
            }
            hr = g_pDIDev2->SetDataFormat(&c_dfDIJoystick2);
            if (FAILED(hr))
            {   MessageBox(NULL,"c_dfDIJoystick2形式の設定に失敗","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;
            }
            hr = g_pDIDev2->SetCooperativeLevel(g_hWnd,DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
            if (FAILED(hr))
            {   MessageBox(NULL,"フォアグラウンド&非排他モードの設定に失敗","Direct Input Error",MB_OK);
                return hr;
            }
            // コールバック関数を使って各軸のモードを設定
            hr = g_pDIDev->EnumObjects(EnumAxesCallback, NULL, DIDFT_AXIS);
            if (FAILED(hr))
            {   MessageBox(NULL,"軸モードの設定に失敗","Direct Input Error",MB_OK);
                return hr;
            }
            // コールバック関数を使って各軸のモードを設定
            hr = g_pDIDev2->EnumObjects(EnumAxesCallback2, NULL, DIDFT_AXIS);
            if (FAILED(hr))
            {   MessageBox(NULL,"軸モードの設定に失敗-2","Direct Input Error",MB_OK);
                return hr;
            }
            // 入力制御開始
            g_pDIDev->Acquire();
            g_pDIDev2->Acquire();
            return S_OK;
        }
        
  4. ジョイスティックを列挙する CALLBACK 関数です。
    最初に見つけたデバイスを g_pDIDev に、次に見つけたデバイスを g_pDIDev2 に設定します。
        // ジョイスティックを列挙する関数
        BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* pContext)
        {   HRESULT hr;
    
            // 列挙されたジョイスティックへのインターフェイスを取得する。
            if (g_pDIDev==NULL)
            {   hr = g_pDInput->CreateDevice(pdidInstance->guidInstance, &g_pDIDev, NULL);
                if (FAILED(hr))     return DIENUM_CONTINUE;
                // ジョイスティックの能力を調べる
                g_diDevCaps.dwSize = sizeof(DIDEVCAPS);
                hr = g_pDIDev->GetCapabilities(&g_diDevCaps);
                if (FAILED(hr))
                {   // ジョイスティック能力の取得に失敗
                    SAFE_RELEASE(g_pDIDev);
                }
                return DIENUM_CONTINUE;
            }
            if (g_pDIDev2==NULL)
            {   hr = g_pDInput->CreateDevice(pdidInstance->guidInstance, &g_pDIDev2, NULL);
                if (FAILED(hr))     return DIENUM_CONTINUE;
                // ジョイスティックの能力を調べる
                g_diDevCaps.dwSize = sizeof(DIDEVCAPS);
                hr = g_pDIDev2->GetCapabilities(&g_diDevCaps);
                if (FAILED(hr))
                {   // ジョイスティック能力の取得に失敗
                    SAFE_RELEASE(g_pDIDev2);
                    return DIENUM_CONTINUE;
                }
            }
            return DIENUM_STOP;
        }
        
  5. ジョイスティックの軸を列挙する CALLBACK 関数(EnumAxesCallback, EnumAxesCallback2) は一個の時と同じです。
  6. コントローラを調べる KeyCheck() 関数です。
    g_pDIDev と g_pDIDev2 のコントローラを調べます。
    △ボタン(○ボタン)でプログラムを終了します。
    dims.lX, dims.lY がコントローラの上下左右ボタンで、最小値(最大値)が設定されます。
        // Controller 入力
        void  KeyCheck()
        {   DIJOYSTATE2 dijs;
            HRESULT     hr;
    
            hr= g_pDIDev->GetDeviceState(sizeof(DIJOYSTATE2), &dijs);
            if (SUCCEEDED(hr))
            {   if (dijs.rgbButtons[0] & 0x80)  PostMessage(g_hWnd,WM_CLOSE,0,0);   //△ボタン
                if (dijs.rgbButtons[1] & 0x80)  PostMessage(g_hWnd,WM_CLOSE,0,0);   //○ボタン
                if (dijs.lX)    Pos.x+= dijs.lX/1000;
                if (dijs.lY)    Pos.y+= dijs.lY/1000;
            }
            else if (hr==DIERR_INPUTLOST)  g_pDIDev->Acquire();
    
            hr= g_pDIDev2->GetDeviceState(sizeof(DIJOYSTATE2), &dijs);
            if (SUCCEEDED(hr))
            {   if (dijs.rgbButtons[0] & 0x80)  PostMessage(g_hWnd,WM_CLOSE,0,0);   //△ボタン
                if (dijs.rgbButtons[1] & 0x80)  PostMessage(g_hWnd,WM_CLOSE,0,0);   //○ボタン
                if (dijs.lX)    Pos2.x+= dijs.lX/1000;
                if (dijs.lY)    Pos2.y+= dijs.lY/1000;
            }
            else if (hr==DIERR_INPUTLOST)  g_pDIDev2->Acquire();
        }
        
  7. Pos の座標に円を、Pos2 の座標に矩形を描画する関数です。
    ウインドウを黒でクリアして円と矩形を描画します。
    プログラムをシンプルにするためにバックバッファを使わないで、そのまま描画しているので画面がチラツキます。
        // 描画処理
        void  Draw(void)
        {   HDC     hdc;
            RECT    rect;
    
            if (Pos.x==WPos.x && Pos.y==WPos.y  && Pos2.x==WPos2.x && Pos2.y==WPos2.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);
            Rectangle(hdc,Pos2.x,Pos2.y,Pos2.x+50,Pos2.y+50);
            ReleaseDC(g_hWnd,hdc);
            WPos= Pos;
            WPos2= Pos2;
        }
        
  8. プログラムを終了する前に、入力制御を終了して領域を開放して下さい。
        // 終了の処理
        void  Cleanup()
        {   if (g_pDIDev)    g_pDIDev->Unacquire(); 
            SAFE_RELEASE(g_pDIDev);
            if (g_pDIDev2)   g_pDIDev2->Unacquire(); 
            SAFE_RELEASE(g_pDIDev2);
            SAFE_RELEASE(g_pDInput);
        }
        
  9. CALLBACK 関数です。
    ウインドウがアクティブになったときに g_bActive を設定して、入力の制御を開始します。
        // ウィンドウ処理
        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();
                        g_pDIDev2->Unacquire();
                    }
                    else
                    {   g_pDIDev->Acquire();
                        g_pDIDev2->Acquire();
                    }
                    return 0L;
                case WM_DESTROY:
                    PostQuitMessage(0);
                    break;
            }
            return DefWindowProc(hWnd,msg,wParam,lParam);
        }
        

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

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

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