DirectInput的學習心得(2)
前言
續前篇DirectInput的學習心得(1),本次紀錄用DirectInput來偵測手把(Gamepad)。為何不直接使用XInuput呢?為了相容一些較舊的手把,就這麼簡單。如果同時使用DirectInput與XInuput時會有一些要注意的,就是DirectInput可以抓到XInuput的手把,本篇會記錄如何濾除,在此做個紀錄。內容
在開始偵測手把時,要先製造出IDirectInput8,如果不知道如何製造,請參照前篇DirectInput的學習心得(1),接著跟上次鍵盤的狀況不太一樣,由於系統鍵盤只會有一個,所以不需要去列舉裝置,但手把可能不只一個,所以透過EnumDevices()來抓出有多少個手把,接著對每個手把做SetCooperativeLevel()與SetDataFormat(),這部分和上次的鍵盤一樣,但這次要多一個步驟,就是透過EnumObjects()來取得類比按鈕的資訊,最後完成後一樣要Acqurie()。來看看範例程式,如下
#include "dinput.h" #include "dinputd.h" struct SEnumJoystickContext { IDirectInput8* pDirectInput; HWND hWnd; std::vector<IDirectInputDevice8>& deviceList; }; BOOL CALLBACK EnumJoystickCallBack(const DIDEVICEINSTANCE* pdidInstance,VOID* pContext) { //Cast context... SEnumJoystickContext* pEnumJoystickContext=(SEnumJoystickContext*)pContext; // LPDIRECTINPUTDEVICE8 pNewDevice=NULL; HRESULT hr=pEnumJoystickContext->pDirectInput->CreateDevice(pdidInstance->guidProduct,&pNewDevice,NULL); if(hr != DI_OK) return DIENUM_STOP; // pEnumJoystickContext->deviceList.push_back(pNewDevice); return DIENUM_CONTINUE; } BOOL CALLBACK EnumObjectsCallBack(const DIDEVICEOBJECTINSTANCE* pdidoi,VOID* pContext) { IDirectInput8* pDevice=(IDirectInput8*)pContext; if(pdidoi->dwType &DIDFT_AXIS) { DIPROPRANGE diprg; diprg.diph.dwSize=sizeof(DIPROPRANGE); diprg.diph.dwHeaderSize=sizeof(DIPROPHEADER); diprg.diph.dwHow=DIPH_BYID; diprg.diph.dwObj=pdidoi->dwType; diprg.lMin=-1000; diprg.lMax=1000; // if(pDevice->SetProperty(DIPROP_RANGE,&diprg.diph)!=DI_OK) return DIENUM_STOP; } return DIENUM_CONTINUE; } void DetectedGamepad(IDirectInputDevice8* pDIDeviceGamepad) { DIJOYSTATE2 gamepadState; pDIDeviceGamepad->GetDeviceState(sizeof(DIJOYSTATE2), &gamepadState); //Get x axis was left if(gamepadState.lX < 0) { //do something... } //Get button 0 state if(gamepadState.rgbButtons[0] &0x80) { //do something... } } int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow ) { //Init main window and IDirectInput8 //... HWND mainWin = initMainWindow; IDirectInput8* pDirectInput = initDirectInput; std::vector<IDirectinputdevice8*> deviceList; // SEnumJoystickContext enumJoystickContext = { pDirectInput, mainWin, deviceList}; pDirectInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoystickCallBack, &enumJoystickContext, DIEDFL_ATTACHEDONLY) for(int i=0;i<deviceList.size();i++) pGamepadDevice->SetCooperativeLevel(mainWin,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); pGamepadDevice->SetDataFormat(&c_dfDIJoystick2); // pGamepadDevice->EnumObjects(EnumObjectsCallBack, pGamepadDevice, DIDFT_ALL); pGamepadDevice->Acquire(); } //Init gamepad end... }
在製造完IDirectInput8後,會看到一個SEnumJoystickContext的Struct與 EnumDevices(),SEnumJoystickContext是要傳給EnumDevices()的參數,再看看EnumDevices()的參數會發現一個CallBack function名為EnumJoystickCallBack,這個CallBack 會在每次被IDirectInput8列許到裝置後被喚起,看看喚起的EnumJoystickCallBack(),有個引數名為"pContext",會發現在開頭就被轉型為SEnumJoystickContext,也會看到另一個引數"pdidInstance",這次必須透過DIDEVICEINSTANCE來製造出IDirectInputDevice8,這個CallBack 的回傳值有"DIENUM_CONTINUE"與"DIENUM_STOP","DIENUM_CONTINUE"表示持持續列舉,"DIENUM_STOP"則代表中止列舉,在EnumDevices()結束後,所有的IDirectInputDevice8都會在"deviceList",接著就初始化每個裝置。初始化裝置和鍵盤很像,一樣是SetCooperativeLevel()與SetDataFormat(),但這次要多一個步驟,就是EnumObjects(),EnumObjects()一樣需要一個CallBack ,這個CallBack 主要用來初始化類比按鍵,最後一樣要Acquire()。偵測的按鍵的部分可以參考範例的DetectedGamepad(),透過GetDeviceState()可以得到"DIJOYSTATE2",裡面就會放置所有按鍵的值,詳細地的說明可以到DIJOYSTATE2 Structure查詢。
如果需要與XInuput同時使用,官方的提供的做法是過濾掉XInuput的裝置,官方提供一個檢查是否為XInuput裝置的function,這個function可以用在EnumJoystickCallBack()裡去做濾除的動作,詳細的程式碼可以在XInput and DirectInput裡找到。
參考資料
XInput and DirectInputDIJOYSTATE2 Structure
沒有留言:
張貼留言