DirectX – 輸入之章

DirectInput 應該是 DirectX 裡面最簡單的介面吧
相較於 Win32 的基本輸入,不用通過 windows 取得,所以相對較快速。

網站上的說法:

    * It enables an application to retrieve data from input devices even when the application is in the background
    * It provides full support for any type of input device, as well as for force feedback
    * Through action mapping, applications can retrieve input data without needing to know what kind of device is being used to generate it.

總而言之,就是有好處才會有人用咩~(不負責宣言)
對了,因為 DirectInput  從 8 版之後就沒更新了,所以底下都使用 8 版,而非 9 版唷!


DirectInput 也是 COM 物件,所以先來介紹主要的介面~

 物件  說明
 IDirectInput8  DirectInput 最主要的介面,其他相關介面從這可取得。
 IDirectInputDevice8  設備的介面,每種設備都有各自的介面。
 IDirectInputEffect  動力回饋(force feedback)介面,某些滑鼠或搖桿會用到。

首先,先引入 dinput.h 並連結 dinput8.lib 和 dxguid.lib (globally unique identifier for DX)

    #include <dinput.h>

接著宣告主要介面和設備介面的指標

    // 主要介面的指標
    LPDIRECTINPUT8 m_pDInput;

    // 鍵盤界面的指標,如需要滑鼠或搖桿,需要再另外增加!
    LPDIRECTINPUTDEVICE8 m_pDIKeyboardDevice;

接著創造主要介面

    DirectInput8Create(inst, DIRECTINPUT_VERSION,
                    IID_IDirectInput8, (void**)&
m_pDInput, NULL);

第一個參數需要視窗的 instance。
第二個參數是 DirectInput 的版本。
第三格參數是我們需要創造的介面種類,來自
dxguid.lib
第四個參數是我們的介面指標。
第五個參數是進階使用,通常設為 NULL。

接著創造鍵盤介面

    m_pDInput->CreateDevice(GUID_SysKeyboard, &m_pDIKeyboardDevice, NULL);

第一個參數是我們要創造的設備種類,鍵盤(GUID_SysKeyboard)、滑鼠(GUID_SysMouse)、搖桿(GUID_Joystick)。
第二個參數是我們的設備介面指標。
第三個是進階使用,通常設為 NULL。

接著設定設備的資料結構

    m_pDIKeyboardDevice->SetDataFormat( &c_dfDIKeyboard );

唯一的參數是我們需要的資料格式,鍵盤(c_dfDIKeyboard)、滑鼠(c_dfDIMouse)、搖桿(c_dfDIJoystick)。

接著設定合作等級

    m_pDIKeyboardDevice->SetCooperativeLevel(hwnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);

第一個參數需要程式的視窗代碼。
第二個參數是合作等級。

合作等即可分兩類,是否獨占與前背景讀取。

 等級  說明
 DISCL_EXCLUSIVE  獨占,就只能自己用啦!
 DISCL_NONEXCLUSIVE  共同使用,不會干擾到其他程式。
 DISCL_FOREGROUND  前景讀取,需要是(Focus)情況下才能讀取資料,失焦時會自動變成無法讀取。
 DISCL_BACKGROUND  不管在縮小或其他程式中,一樣可讀取資料。
 DISCL_NOWINKEY  取消 Windows 商標按鍵的作用。

OK!都準備完成了,接下來開始使用吧!



使用鍵盤設備

第一件事情就是先取得設備

    m_pDIKeyboar
dDevice
->Acquire();

並不需要每次讀取資料就取得一次,除非第一次或喪失該設備(縮小或Alt-Tab之類)才須取得。

我們需要一個 256 Byte 的緩衝區,使用 GetDeviceState 來接收鍵盤資訊

    BYTE keys[256];

    // 使用前先初使化緩衝區
    ZeroMemory(keys, sizeof(keys) );
   
m_pDIKeyboardDevice->GetDeviceState( sizeof(keys), keys );

和 Win32 一樣,每個按鍵都有一個巨集,在  DirectInput 裡面是以 DIK_ 開頭的。
例如按鍵 A 定義為 DIK_A,Esc 鍵則定義為 DIK_ESCAPE。

偵測 Esc 被按下

    if (keys[DIK_ESCAPE] & 0x80)
        //  Esc 被按下

偵測 X 被按下

    if (keys[DIK_X] & 0x80)
        // x 被按下

最後,使用完之後可別忘記要釋放他們唷!

    if (m_pDIKeyboardDevice)
    {
       
m_pDIKeyboardDevice->Unacquire();
       
m_pDIKeyboardDevice->Release();
    }

    if (m_pDInput)
       
m_pDInput->Release();



如何偵測設備喪失!

當我們在取得設備資訊時,如果該設備 lost 掉,會回傳 DIERR_INPUTLOST 的錯誤碼。
利用回圈檢查,直到取得為止才繼續執行。

    hr = m_pDIKeyboardDevice->GetDeviceState( sizeof(keys), keys );

    if (FAILED(hr))
    {
        hr = m_pDIKeyboardDevice->Acquire();

        while( hr == DIERR_INPUTLOST )
        {
            hr = m_pDIKeyboardDevice->Acquire();
        }

        // 成功取得後再取得一次資訊
        m_pDIKeyboardDevice->GetDeviceState( sizeof(keys), keys );
    }



使用滑鼠設備

前置作業都差不多,參數不同而已,不贅述囉~

    m_pDInput->CreateDevice(GUID_SysMouse, &m_pDIMouseDevice, NUL
L
);
   
m_pDIMouseDevice->SetDataFormat(&c_dfDIMouse2);
   
m_pDIMouseDevice->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);

如同鍵盤一樣,需要一塊緩衝區來使用 GetDeviceState 讀取資料

    DIMOUSESTATE2 m_mouseState;
    ZeroMemory( &m_mouseState, sizeof(m_mouseState) );
   
m_pDIMouseDevice->GetDeviceState( sizeof(DIMOUSESTATE2), &m_mouseState );

同樣要檢查是否回傳 DIERR_INPUTLOST 嘿!
注意,這裏使用 c_dfDIMouse2 和  DIMOUSESTATE2 ,是擁有八顆鍵的滑鼠(應該…)

DIMOUSESTATE2  的資料結構:

    typedef struct _DIMOUSESTATE2 {
        LONG    lX;   // X
的偏移量
        LONG    lY;   // Y
的偏移量
        LONG    lZ;    // Z軸的偏移量
        BYTE    rgbButtons[8];   // 每個按鈕的狀態
    } DIMOUSESTATE2, *LPDIMOUSESTATE2;

記住,是偏移量,而不是位置
所以通常在初始化時會先利用 GetCursorPos 或者 WM_MOUSEMOVE 時取得一開始的位置
然後在隨時更新座標即可!

    // 宣告兩個變數儲存位置
    long g_MouseXPos = 0;
    long g_MouseYPos = 0;

    // 遊戲迴圈
    void Run()
    {
       // 讀取滑鼠資料

       // 更新滑鼠的絕對座標
      
g_MouseXPos += m_mouseState.lX;
       g_MouseYPos += m_mouseState.lY;

       // 其他程式碼
    }

檢查某按鈕狀態

    if (m_mouseState.rgbButtons[0] & 0x80)
       // 左鍵(0)被按下

最後,還是別忘記用完要釋放他們唷!



◎參考:
       http://www.toymaker.info/
       2D/3D RPG角色扮演遊戲程式設計 – 使用DirectX / Jim Adams著
   
當然,還有一個 搖桿設備 的部份,不過目前還用不到,而且前置動作很雜,以後用到在補上吧XD

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.