掌握 DirectX 和 DirectInput ——力反饋游戲桿
Jason Clark
不知不覺中, Windows 下的游戲和多媒體程序已經(jīng)開始流行。硬件變得越來越快, Windows 也變得更加靈活。自從 Microsoft 發(fā)布了 DirectX ,游戲開發(fā)人員對其它平臺已經(jīng)越來越不感興趣了。許多游戲開發(fā)者也已經(jīng)將他們的開發(fā)工作完全移植到了 Windows 下。
為 PC 開發(fā)游戲從來就沒有輕松過。從無數(shù)種顯示卡和聲卡中,開發(fā)者學(xué)會了在功能性和兼容性之間平衡的藝術(shù)。他們不得不處理象頁面切換、段內(nèi)存結(jié)構(gòu)和位操作這樣令人討厭的問題。并且隨著多人游戲的流行,開發(fā)者必須同時處理象網(wǎng)絡(luò)和通信等事項。 DirectX 引入后,游戲開發(fā)者變得輕松了。通過為開發(fā)者提供的 DirectX 對象,絕大多數(shù)討厭的工作已經(jīng)被簡化了。
基于 DirectX 的程序是普通的 Windows 程序嗎?必須懂得 COM 嗎?為簡單的程序值得使用 DirectX 嗎?必須使用 DirectX 的全部組件嗎?這樣的問題肯定還有更多。
本文將首先介紹 DirectX ,然后介紹 DirectX 的一個組件 DirectInput 的使用。演示程序說明了 DirectInput 的用法,著重介紹了其強大的反饋功能。
DirectX 揭密
DirectX 是一套為 Windows 程序提供對系統(tǒng)硬件更親密控制的組件。(表 1 列出了 DirectX 5.0 的組件及其作用)。那么,親密控制是什么意思呢?
表 1 : DirectX 5.0 的組件
|
組件 |
用途 |
|
DirectDraw |
高速 2D 圖象 |
|
DirectSound |
短響應(yīng)時間聲音輸出 |
|
Direct3D |
高速 3D 圖象 |
|
DirectInput |
面向游戲的對游戲桿和其它輸入設(shè)備的訪問 |
|
DirectSetup |
方便的安裝 DirectX 組件 |
|
DirectPlay |
面向游戲的通信和網(wǎng)絡(luò)支持 |
|
DirectShow |
視頻流支持 |
|
DirectAnimation |
動畫錄放支持 |
DirectX 提供的硬件控制常常被描述成底層控制,這會使人聯(lián)想起位操作和其它討厭的事情。實際上, DirectX 組件包含許多高層 API ,使得象復(fù)制位圖和播放聲音等復(fù)雜的工作變得相當(dāng)簡單。用“為程序提供比過去更好的對硬件的控制”來形容 DirectX 更準(zhǔn)確。這在 Windows 中是一個顯著的特性,因為在 Windows 中,資源是共享的,并由操作系統(tǒng)控制。
DirectX 組件遵守稱為 COM 的二進(jìn)制對象的工業(yè)標(biāo)準(zhǔn)。
開始 DirectX
下面從 DirectX 的安裝開始講起。大多數(shù)情況下,某個好玩的游戲就會為系統(tǒng)安裝 DirectX 。為得到最新的版本,應(yīng)該從最新的 Microsoft Platform SDK 中將 DirectX 安裝到系統(tǒng)中。可以在 http://www.microsoft.com/msdn 站點或者 MSDN 光盤中找到 platform SDK 。缺省情況下, Microsoft Platform SDK 被安裝到缺省驅(qū)動器根目錄下的 /MSSDK 目錄中。 DirectX 的頭文件安裝在 /MSSDK/INCLUDE 目錄中, Lib 文件安裝在 /MSSDK/LIB 目錄中。
Platform SDK 包含了一些非常好的 DirectX 例子和文檔。早期發(fā)布的 DirectX 文檔非常粗略而且有些是錯誤的,現(xiàn)在的版本已經(jīng)極大地改正了這一問題。最好要熟悉這些文檔。
現(xiàn)在已經(jīng)為安裝利用 DirectX 的程序做好了準(zhǔn)備。所幸的是,不必一次就處理 DirectX 的全部功能。 DirectX 是一套可以分別使用的組件。實際上,在編程概念中, DirectX 的不同部分互相沒有聯(lián)系。它們僅僅是具有相同的設(shè)計風(fēng)格和目標(biāo):使 Windows 的游戲編程變得容易。
使用 DirectX 組件的程序有什么特殊的地方嗎?根本沒有。使用 DirectX 組件的程序是基于 Win32 的程序,它們使用普通 Win32 API 集,并且可以訪問所有可以獲得的操作系統(tǒng)工具。實際上, DirectX 既可以用于 GUI 程序,也可以用于控制臺程序。可以直接用 Petzold-style SDK 編程開發(fā)程序,也可以用基本類庫,如 MFC 。總的說,唯一的要求是大多數(shù) DirectX 組件在程序中需要 HWND ,所以至少要有一個窗口。
雖然 DirectX 組件是分離的,但是每個組件的實現(xiàn)風(fēng)格和使用都是相同的。 DirectInput 是學(xué)習(xí) DirectX 的非常好的出發(fā)點,原因是 DirectInput 是最簡單的組件之一。
用力
以后在游戲中要“用力”,這是電影《星球大戰(zhàn)》中的說法,因為 DirectInput 中加入了相當(dāng)令人陶醉的力反饋支持。 DirectX 5.0 以前, DirectInput 支持從鼠標(biāo)和鍵盤讀取輸入,這是一個有用但卻令人厭煩的特性。 DirectX 5.0 中, DirectInput 被擴充到支持具有以物理力的形式向用戶傳播反饋的能力的設(shè)備。
如果不能立即理解上面的內(nèi)容,下面就用一個游戲進(jìn)行解釋。假設(shè)你剛啟動了你最喜歡的超現(xiàn)實 3D 越野賽車游戲,正手握力反饋游戲桿。在起跑線上,你可以聽到賽車引擎的空轉(zhuǎn)聲,同時也能夠通過游戲桿感覺到賽車引擎的空轉(zhuǎn)!比賽開始后,你可以感覺到引擎高速旋轉(zhuǎn)的嗡嗡震動。當(dāng)行駛到賽程中崎嶇的地段時,你將會不停的感覺到電子碰撞。賽車在整個賽場上撞來撞去,你的游戲桿也會如此。賽車車輪卡在車轍中導(dǎo)致賽車被拉向左邊,游戲桿也會被拉向左邊!整個過程中你可以感覺到每次顛簸、刮擦、撞擊和撞毀。
現(xiàn)在,帶有支持 DirectInput 的 Windows 驅(qū)動程序的唯一的力反饋設(shè)備是 Microsoft 的 SideWinder Force Feedback Pro 。這一現(xiàn)狀不會持續(xù)太久,新設(shè)備以及現(xiàn)有設(shè)備的新驅(qū)動程序很快就會進(jìn)入市場。
剖析 DirectInput
DirectInput 由三個對象組成: DirectInput, DirectInputDevice, 和 DirectInputEffect ( 見表 2) 。 DirectInput 是一個高層的對象,通過 DirectInput 對象可以對相關(guān)的輸入設(shè)備進(jìn)行基本的初始化和查找。 DirectInput 對象最終用來創(chuàng)建低層的 DirectInputDevice 對象。 DirectX 中的每個主要組件都采用相同的方法,首先創(chuàng)建高層對象,如 DirectInput 或 DirectSound 對象,然后創(chuàng)建低層對象與硬件進(jìn)行實際的通信。
表 2: DirectInput 對象
|
對象 |
說明 |
|
DirectInput |
封裝高層 DirectInput 功能,列舉設(shè)備并用來創(chuàng)建 DirectInputDevice 對象。 |
|
DirectInputDevice |
與物理輸入設(shè)備的接口,例如游戲桿,包括收集和設(shè)置設(shè)備狀態(tài)信息的接口,并且用來創(chuàng)建 DirectInputEffect 對象 ( 對于力反饋設(shè)備 ) 。 |
|
DirectInputEffect |
封裝能夠在力反饋設(shè)備上“播放”的簡單效果,提供啟動、停止和設(shè)置力反饋效果等功能。 |
DirectInput 對象是三個對象中最容易理解的。實際上,它在一個接口形式 IDirectInput ( 見表 3) 中只提供五個函數(shù)。這是 DirectInput 的一個非常重要的部分,因為這是出發(fā)點。
表 3 : IdirectInput 接口
|
成員函數(shù) |
說明 |
|
CreateDevice |
創(chuàng)建一個 DirectInputDevice 對象并返回一個指向其 IdirectInputDevice 接口的指針。 |
|
EnumDevices |
為找到的與給定標(biāo)準(zhǔn)匹配的每個設(shè)備調(diào)用一個回調(diào)函數(shù),每個回調(diào)函數(shù)提供一個 GUID ,可以用在 CreateDevice 中創(chuàng)建 DirectInputDevice 對象。 |
|
GetDeviceStatus |
測試物理設(shè)備是否連接到系統(tǒng)。 |
|
Initialize |
如果 DirectInput 對象是使用 CoCreateInstance 創(chuàng)建的,那么在使用前必須調(diào)用 Initialize 成員。如果 DirectInput 對象是使用 DirectInputCreate 創(chuàng)建的,那么就已經(jīng)初始化過了。 |
|
RunControlPanel |
為設(shè)備運行 Windows Control Panel 程序,讓用戶安裝新設(shè)備或者更改已有設(shè)備的配置。游戲桿校準(zhǔn)可以在此處做。 |
創(chuàng)建 DirectInput 對象
為了創(chuàng)建 DirectInput 對象并得到其 IdirectInput 接口指針,應(yīng)該在程序初始化階段使用兩種方法之一完成。
第一種方法相當(dāng)簡單。 DirectX 提供了一個助手函數(shù) DirectInputCreate 來創(chuàng)建并初始化 DirectInput 對象。它與所有 DirectInput 的函數(shù)、接口和宏定義都在頭文件 DINPUT.H 中聲明。實際的函數(shù)體在 DINPUT.LIB 文件中。
DirectInputCreate 如下定義:
HRESULT WINAPI DirectInputCreate(
HINSTANCE hinst,
DWORD dwVersion,
LPDIRECTINPUT * lplpDirectInput,
LPUNKNOWN punkOuter
);
第一個參數(shù)是應(yīng)用程序的實例。第二個參數(shù)是程序需要的 DirectInput 版本,通常使用 DIRECTINPUT_VERSION 宏,定義為當(dāng)前版本。第三個參數(shù)最重要,如果對 COM 非常陌生的化就很難理解,它是指向 IdirectInput 接口的指針的地址。程序中應(yīng)該定義一個 LPDIRECTINPUT 類型的變量(可以是全局的)并將其地址作為第三個參數(shù)傳遞給 DirectInputCreate 。
最后一個參數(shù)叫作 punkOuter ,與 COM 技術(shù)中的聚合有關(guān),可以用 NULL 安全的忽略。返回值是一個 HRESULT ,是 COM 的標(biāo)準(zhǔn)返回類型,可以將返回值與可能的返回值比較,也可以使用 COM 宏定義 SUCCESS 或 FAILED 來檢查。
使用 DirectInputCreate 能夠容易地創(chuàng)建高層對象并得到其主接口指針。這是 DirectX 的又一個設(shè)計方法,每個 DirectX 組件都提供助手函數(shù)來創(chuàng)建高層對象,例如 DirectInputCreate 或 DirectDrawCreate 。在程序中可以用這些助手函數(shù)創(chuàng)建 DirectX 對象,然而,這些函數(shù)實際上創(chuàng)建的是 COM 對象。這個工作也可以用叫作 CoCreateInstance 的標(biāo)準(zhǔn) Win32 API 函數(shù)來完成。這就引出了創(chuàng)建 DirectInput 對象的第二中方法。
在 Win32 中用 CoCreateInstance 創(chuàng)建 COM 對象非常普遍。如果程序中已經(jīng)使用 CoCreateInstance 創(chuàng)建了其他 COM 對象,開發(fā)者可能就會希望也用它來創(chuàng)建 DirectX 對象。因為 COM 對象在安裝時就在系統(tǒng)中注冊過,所以唯一需要知道的就是對象的 GUID ,用它來創(chuàng)建一個實例。創(chuàng)建 DirectX 對象需要的全部 GUID 都在頭文件中聲明,并在庫文件 DXGUID.LIB 中定義。可以將一個預(yù)定義的 GUID 傳遞給 CoCreateInstance ,讓 Windows 為你創(chuàng)建對象。
CoCreateInstance 定義如下:
STDAPI CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv
);
第一個參數(shù)是要創(chuàng)建對象的 GUID , DirectX 定義的 GUID 是叫作 CLSID_DirectInput 的 GUID 結(jié)構(gòu)變量。第二個參數(shù)是熟悉的 pUnkOuter ,同樣可以用 NULL 忽略。第三個參數(shù) dwClsContext 定義 COM 對象在何處創(chuàng)建, DirectX 只支持進(jìn)程內(nèi)服務(wù)器,所以必須使用 CLSCTX_INPROC_SERVER 。
第四個參數(shù)是兩種方法真正的不同之處。記住 COM 對象對外提供接口,與對象本身一樣,接口也用 GUID 識別。使用第一種方法,不能選擇得到的接口,總是得到 IdirectInput 。使用 CoCreateInstance 可以請求對象所支持的任何接口,方法是使用為接口預(yù)定義的 GUID 。但是在 DirectInput 這是沒有意義的,因為 DirectInput 對象的唯一有用的接口就是 IdirectInput 。其它 DirectX 組件支持多個有用的接口。(例如, DirectDraw 對象可以用 IdirectDraw 或 IDirectDraw2 接口操作。)
最后一個參數(shù)是程序中接口指針變量的實際地址。
現(xiàn)在就擁有了對象和對象的一個接口。 CoCreateInstance 方法還需要另外一步:必須要首先調(diào)用一個接口函數(shù)初始化對象。 DirectInputCreate 提供的是一個已經(jīng)初始化過的 DirectInput 對象,但 CoCreateInstance 沒有特定于 DirectInput 的認(rèn)識,因此必須調(diào)用 IdirectInput 接口的初始化成員函數(shù)。假設(shè)如下定義 IdirectInput 接口指針變量:
LPDIRECTINPUT g_lpDI
可以如下調(diào)用初始化函數(shù):
g_lpDI->Initialize( hInstance, DIRECTINPUT_VERSION);
既然選擇采取這種標(biāo)準(zhǔn)方法創(chuàng)建對象,就不得不注意 COM 需要的其他標(biāo)準(zhǔn),例如需要調(diào)用 CoInitialize 和 CoUninitialize 。
使用 DirectInput 對象
一旦擁有了 DirectInput 對象,就可以用它來創(chuàng)建 DirectInputDevice 對象,來管理系統(tǒng)中特定的設(shè)備。創(chuàng)建 DirectInputDevice 對象要使用 CreateDevice 函數(shù),它是作為 IdirectInput 接口一部分的五個函數(shù)之一。 CreateDevice 需要所請求設(shè)備的 GUID ,返回新 DirectInputDevice 對象的 IdirectInputDevice 接口指針。
HRESULT CreateDevice(
REFGUID rguid,
LPDIRECTINPUTDEVICE *lplpDirectInputDevice,
LPUNKNOWN pUnkOuter
);
這些內(nèi)容看起來很熟悉,因為它與 CoCreateInstance 和 DirectInputCreate 類似。但是,現(xiàn)在還沒有完全準(zhǔn)備好開始 DirectInputDevice 對象,原因是在創(chuàng)建 DirectInputDevice 對象前需要該設(shè)備的 GUID 。
DirectInput 庫為創(chuàng)建 DirectInputDevice 對象預(yù)定義了兩個 GUID : GUID_SysKeyboard 和 GUID_SysMouse 。將兩者之一直接傳遞給 CreateDevice 函數(shù),就會得到相應(yīng)設(shè)備的 DirectInputDevice 對象。
注意,令人感到奇怪的是缺少對游戲桿的預(yù)定義 GUID 。在 Windows 中,通常都有系統(tǒng)鍵盤和系統(tǒng)鼠標(biāo),另一方面,系統(tǒng)本身并不使用游戲桿。可以安裝一個或者多個游戲桿,但系統(tǒng)管理的范圍只限于驅(qū)動程序級。系統(tǒng)并為這些設(shè)備指定特殊的系統(tǒng)狀態(tài),也不會在日常事務(wù)中使用這些設(shè)備。因此,為游戲桿定義 GUID 對 DirectInput 來說是不合理的。
那么,如何才能找到與系統(tǒng)連接的游戲桿的 GUID 呢?要得到它們,必須要列舉設(shè)備。列舉系統(tǒng)設(shè)備和性能在 DirectX 中相當(dāng)普遍。要列舉系統(tǒng)中的輸入設(shè)備,需要使用 EnumDevices 函數(shù)。 EnumDevices 是 IdirectInput 接口的一部分,如下定義:
HRESULT EnumDevices(
DWORD dwDevType,
LPDIENUMCALLBACK lpCallback,
LPVOID pvRef,
DWORD dwFlags
);
注意此函數(shù)與 Windows 中其它列舉 API 相同,例如 EnumWindows 。第二個參數(shù)是一個回調(diào)函數(shù)。第三個參數(shù)是程序中定義的 32 位值。第一個參數(shù)是想要列舉的設(shè)備類型,對游戲桿來說,是 DIDEVTYPE_JOYSTICK (全部的設(shè)備類型列在表 4 中)。最后一個參數(shù)是詳細(xì)描述想要列舉的設(shè)備的標(biāo)志。現(xiàn)在支持的標(biāo)志是 DIEDFL_ATTACHEDONLY 和 DIEDFL_ALLDEVICES (這兩個標(biāo)志是互斥獨占的),此外還有 DIEDFL_FORCEFEEDBACK ,此標(biāo)志表示力反饋設(shè)備,能夠和另兩個標(biāo)志位或操作。
圖 4 :定義列舉的輸入設(shè)備
以下定義的值可以傳遞給 EnumDevices 來選擇列舉哪種類型的輸入設(shè)備。另外也支持子類型,見 SDK 中 DIDEVICEINSTANCE 結(jié)構(gòu)的文檔。
|
值 |
說明 |
|
DIDEVTYPE_MOUSE |
列舉鼠標(biāo)設(shè)備 ( 標(biāo)準(zhǔn)、軌跡球等 ) |
|
DIDEVTYPE_KEYBOARD |
列舉鍵盤設(shè)備 ( 標(biāo)準(zhǔn)、鍵區(qū)等 ) |
|
DIDEVTYPE_JOYSTICK |
列舉游戲桿設(shè)備 ( 操縱桿、操縱輪、方向舵等 ) |
|
DIDEVTYPE_DEVICE |
列舉其它設(shè)備 |
當(dāng) EnumDevices 列舉系統(tǒng)中的輸入設(shè)備時,反復(fù)地調(diào)用回調(diào)函數(shù)。回調(diào)函數(shù)定義如下:
BOOL CALLBACK EnumProc(LPCDIDEVICEINSTANCE lpddi,LPVOID pvRef) ;
因為回調(diào)函數(shù)是由用戶程序定義并傳遞給 EnumDevices 的,所以是調(diào)用 CreateDevice 的最合適地方,直到創(chuàng)建了滿足需要的足夠 DirectInputDevice 對象為止。但是回調(diào)函數(shù)并非一定要如此實現(xiàn),可以簡單的將列舉設(shè)備的所有 GUID 保存在一個表中,在以后的代碼中使用。
回調(diào)函數(shù)接受兩個參數(shù)。第二個參數(shù)是程序定義的傳遞給 EnumDevices 的 32 位值。更重要的是,第一個參數(shù)傳遞指向一個結(jié)構(gòu)的指針,該結(jié)構(gòu)包含關(guān)于能夠與列舉標(biāo)準(zhǔn)匹配的單個設(shè)備的許多信息。這是一個 DIDEVICEINSTANCE 結(jié)構(gòu)。此結(jié)構(gòu)中最重要的一條信息是設(shè)備的 GUID ,保存在結(jié)構(gòu)的 guidInstance 成員中。
當(dāng)程序中完全完成 DirectInput 有關(guān)的工作后,就應(yīng)該調(diào)用 IdirectInput 接口的 Release 成員。這就告訴 DirectInput 對象可以釋放自己了。在 DirectX 中,最好養(yǎng)成釋放對象的習(xí)慣,從低層對象開始,到高層對象結(jié)束。正常情況下程序會作為清除或者關(guān)閉的例行公事的一部分調(diào)用 Release 。這是使用每個 DirectX 組件的必要步驟,也是使用每個 COM 組件的必要步驟。
現(xiàn)在已經(jīng)用 CreateDevice 成員函數(shù)獲得了 DirectInputDevice 對象的一個接口,為開始處理與系統(tǒng)連接的實際物理設(shè)備做好了準(zhǔn)備。
使用 DirectInputDevice 對象
DirectInputDevice 對象的每個實例都與系統(tǒng)中的特定設(shè)備相關(guān)。此對象提供了對系統(tǒng)硬件更多的控制和能力,從而使 DirectX 的允諾實現(xiàn)。下面討論擁有了 DirectInputDevice 對象后下一步干什么。
擁有了 IdirectInputDevice 接口的一個接口指針,現(xiàn)在干什么?首先,設(shè)置設(shè)備的數(shù)據(jù)格式。通過調(diào)用 SetDataFormat 來完成,該函數(shù)是一個接口成員函數(shù)。設(shè)置數(shù)據(jù)格式包括無數(shù)可能的決定,包括軸信息、相對或絕對坐標(biāo)信息、等等。所有這些細(xì)節(jié)通過一個叫作 DIDATAFORMAT 的結(jié)構(gòu)傳遞給此函數(shù)。實際上, SetDataFormat 唯一的參數(shù)就是指向此結(jié)構(gòu)的指針。
填寫這個結(jié)構(gòu)的細(xì)節(jié)會使人發(fā)憷。值得感謝的是這一工作并不是必須的,因為 DirectInput 已經(jīng)定義了幾個 DIDATAFORMAT 結(jié)構(gòu)變量,可以用于比較普通的輸入設(shè)備: c_dfDIKeyboard, c_dfDIMouse, c_dfDIJoystick, 和 c_dfDIJoystick2 。為普通的力反饋游戲桿設(shè)置數(shù)據(jù)格式,可以使用下面的調(diào)用形式:
lpdid->SetDataFormat( &c_dfDIJoystick ) ;
在此例中, lpdid 是指向 IdirectInputDevice 接口的指針。
設(shè)置完設(shè)備對象的數(shù)據(jù)格式后,就需要設(shè)置設(shè)備的協(xié)作級別。因為協(xié)作級別在整個 DirectX 中很常見,所以這里要做一下說明。大多數(shù)直接處理系統(tǒng)硬件的 DirectX 對象在接口的成員中都有一個叫作 SetCooperativeLevel 函數(shù)。這個函數(shù)很重要,因為它定義了程序操縱與系統(tǒng)中其它進(jìn)程有關(guān)的硬件的控制級別。同其它 DirectX 對象一樣,只有設(shè)置了協(xié)作級別才能使 DirectInputDevice 對象工作。要理解協(xié)作級別,就需要熟悉 Acquire 函數(shù)。調(diào)用此函數(shù)是為了獲得對物理設(shè)備的實際訪問(不要和邏輯上的 DirectInputDevice 對象混了)。相反的, Unacquire 函數(shù)釋放對物理設(shè)備的訪問。
下面是函數(shù) SetCooperativeLevel 的定義:
HRESULT SetCooperativeLevel(
HWND hwnd,
DWORD dwFlags
);
hwnd 是程序的主窗口。標(biāo)志是下面一些值的或操作的結(jié)合: DISCL_BACKGROUND, DISCL_FOREGROUND, DISCL_EXCLUSIVE, DISCL_ NONEXCLUSIVE 。
如果標(biāo)志參數(shù)中或上了 DISCL_EXCLUSIVE ,則當(dāng)獲得設(shè)備后本程序就成為唯一允許訪問該物理設(shè)備的進(jìn)程。另一方面,如果選擇了 DISCL_NONEXCLUSIVE ,那么系統(tǒng)中可以有多個進(jìn)程同時協(xié)作獲得和使用該設(shè)備。如果或上了 DISCL_BACKGROUND ,程序?qū)⒉粫ノ锢碓O(shè)備。然而,象 Ctrl+Alt+Del 組合鍵被按下這樣的系統(tǒng)事件仍然能夠隱含地 “unacquire” 程序中的設(shè)備。如果使用了 DISCL_ FOREGROUND ,當(dāng)不是活動窗口時,程序?qū)詣俞尫盼锢碓O(shè)備。這就是將程序主窗口句柄傳遞給 SetCooperativeLevel 的意義。 DirectX 根據(jù)窗口是否是系統(tǒng)當(dāng)前活動窗口自動調(diào)整設(shè)備共享。
那么所有這些值的意義是什么呢?下面舉個例子說明。如果力反饋游戲桿的協(xié)作模式是 DISCL_FOREGROUND | DISCL_EXCLUSIVE ,那么只要程序處于活動狀態(tài),就能夠從游戲桿讀數(shù)據(jù)并播放力反饋效果(力反饋需要 exclusive-level 協(xié)作)。只要用戶一選擇其它程序,程序就失去對物理設(shè)備的控制,新激活的程序就能夠訪問該設(shè)備。這意味著在調(diào)試程序時,如果切換到調(diào)試器窗口,程序就會因為窗口變?yōu)榉腔顒拥亩τ螒驐U的控制。
如果將同一游戲桿的協(xié)作級別設(shè)為 DISCL_BACKGROUND | DISCL_EXCLUSIVE 將會是什么情況呢?程序?qū)袝r間都能訪問游戲桿,不管窗口的狀態(tài)。但是現(xiàn)在系統(tǒng)中其它進(jìn)程就不能獲得游戲桿,除非程序釋放了游戲桿,不管用戶在做什么!
非常明顯,在正式發(fā)布的產(chǎn)品中應(yīng)該使用 DISCL_FOREGROUND | DISCL_EXCLUSIVE ,而在調(diào)試版本中應(yīng)該使用 DISCL_BACKGROUND|DISCL_EXCLUSIVE 。但是也不總是這樣選擇。例如,如果設(shè)備是系統(tǒng)鍵盤,那么 DirectInputDevice 想獨占使用而調(diào)用 SetCooperativeLevel 將會失敗。這是因為操作系統(tǒng)想要允許用戶自由地從一個程序切換到另一個程序。類似的, DirectInputDevice 不會允許以協(xié)作級別 DISCL_BACKGROUND|DISCL_EXCLUSIVE 請求系統(tǒng)鼠標(biāo)。 Windows 不希望一個程序能夠完全將用戶與操作系統(tǒng)的聯(lián)系切斷。
在能夠從物理設(shè)備讀取信息或向物理設(shè)備發(fā)送信息之前,必須要用 Acquire 獲得設(shè)備。在臨時或永久結(jié)束設(shè)備使用時要明確地使用 Unacquire 函數(shù)釋放設(shè)備。但 Unacquire 并不是失去設(shè)備控制的唯一方法。
如果設(shè)置協(xié)作級別時使用 DISCL_FOREGROUND 標(biāo)志,那么程序的主窗口不再是系統(tǒng)中的活動窗口時設(shè)備將被明確釋放。這就是說,在程序調(diào)用 Acquire 和實際試圖從設(shè)備讀取信息之間,能夠失去對設(shè)備的占有。所以需要檢查返回值來捕捉這樣的錯誤,并準(zhǔn)備好在任何時間重新獲得該設(shè)備。
關(guān)于 Acquire 和 Unacquire 的決定性要點:當(dāng)程序獲得獨占協(xié)作級別的設(shè)備時, DirectX 擁有該設(shè)備。例如,如果鼠標(biāo)被 DirectX (獨占)獲得,那么程序窗口中的按鈕就不會對鼠標(biāo)做出響應(yīng)。這就是說,如果想讓 Windows 對設(shè)備響應(yīng),就應(yīng)該釋放該設(shè)備。換句話說,如果不想讓 DirectInput 從設(shè)備中讀取數(shù)據(jù),就調(diào)用 Unacquire 。
設(shè)置完設(shè)備的協(xié)作級別后,接著應(yīng)該為設(shè)備配置其它設(shè)置。獲得了設(shè)備后,接著就應(yīng)該開始使用 GetDeviceState 函數(shù)輪流檢測輸入的數(shù)據(jù)。當(dāng)完成與設(shè)備對象的操作后,調(diào)用 Unacquire 釋放 DirectInputDevice 對象。設(shè)備與設(shè)備之間存在細(xì)節(jié)上的差別;下面講解游戲桿和鍵盤,應(yīng)該能為從其它設(shè)備讀取輸入提供足夠的基礎(chǔ)知識。
鍵盤
鍵盤是到目前為止最容易讀取的設(shè)備。實際上,設(shè)置完數(shù)據(jù)格式、協(xié)作級別、獲得設(shè)備以后,就可以讀取鍵盤狀態(tài)了。讀取鍵盤狀態(tài)要使用 IdirectInputDevice 接口的 GetDeviceState 成員。 GetDeviceState 用關(guān)于物理設(shè)備的狀態(tài)信息組裝一個結(jié)構(gòu),所組裝結(jié)構(gòu)的類型由前面對 SetDataFormat 的調(diào)用決定。對鍵盤來說,此數(shù)據(jù)結(jié)構(gòu)是一個簡單的 256 個字節(jié)組成的數(shù)組。每個字節(jié)對應(yīng)于鍵盤上的一個鍵,如果某個鍵按下,相應(yīng)字節(jié)的高位就被設(shè)置。
DirectInput 定義了一套以 DIK_XXX 為前綴的常量,這些常量可以用來索引字節(jié)數(shù)組以找到關(guān)于特定鍵的數(shù)據(jù)。例如,如果要檢查右 Shif 鍵當(dāng)前是否按下,可以使用 DIK_RSHIFT 定義:
GetDeviceState(256,(LPVOID) cKeyboardData) ;
if(cKeyboardData[DIK_ RSHIFT]&0x80)
DoWhatever() ;
CKeyboardData 是 256 個字節(jié)的緩沖區(qū)。幾乎就是這么簡單,但是要記住,不管 GetDeviceState 在何時返回 DIERR_INPUTLOST ,就必須使用 Acquire 獲得設(shè)備。這種情況發(fā)生在每次用戶從程序切換離開的時候。
還有一點很重要,就是能夠請求 DirectInput 緩沖鍵盤信息。這要求提供一個緩沖區(qū)并使用 SetProperty 為設(shè)備設(shè)置緩沖區(qū)大小。在本文中沒有篇幅討論這一技術(shù),但這一技術(shù)在程序不能相當(dāng)頻繁的檢查鍵盤狀態(tài)時非常有用。用戶有可能在程序中兩次 GetDeviceState 調(diào)用之間按下又松開了一個鍵,如果 DirectInput 不緩沖鍵盤數(shù)據(jù)的化,這種擊鍵動作就丟失了。
游戲桿
游戲桿非常好玩。與其好聽的名稱( Joystick ——原意為歡樂桿)相符,這種設(shè)備為游戲體驗添加了許多樂趣,同時也為程序員的體驗添加了一些東西。正常情況下,通過調(diào)用 IdirectInput 接口的 CreateDevice 成員得到 IdirectInputDevice 接口(和對象),這對游戲桿也適用。
但是開發(fā)人員都希望立即將接口升級到 IDirectInputDevice2 ,那么可以象下面這樣使用 QueryInterface 調(diào)用請求 CreateDevice 返回新的接口:
hr = lpDIDeviceJoystickTemp->QueryInterface( IID_IDirectInputDevice2,
(void **) &g_lpDIDeviceJoystick);
如果成功,就可以釋放原來的接口,開始使用漂亮的新 IDirectInputDevice2 接口。但是為什么要這么做呢? IDirectInputDevice2 接口提供 IdirectInputDevice 的所有功能,而且還有另外兩個重要特性:支持查詢設(shè)備和支持力反饋設(shè)備。
其次,需要設(shè)置上的一些考慮。還記得 SetDataFormat 定義了 GetDeviceState 返回的數(shù)據(jù)的類型。對于游戲桿設(shè)備,使用 c_dfDIJoystick 或 c_dfDIJoystick2 兩個預(yù)定義變量之一,將返回數(shù)據(jù)的類型設(shè)置為 DIJOYSTATE 或 DIJOYSTATE2 結(jié)構(gòu)。選擇哪種主要取決于要使用游戲桿哪種類型的特性。瀏覽這些結(jié)構(gòu)中的成員應(yīng)該對弄清這個問題有幫助。
同所有輸入設(shè)備一樣,要為游戲桿設(shè)置數(shù)據(jù)格式和協(xié)作級別。游戲桿往往比鍵盤需要更多一點注意。這是因為現(xiàn)在還幾乎沒有功能完美的游戲桿,所以程序應(yīng)該檢查以確保控制的設(shè)備能滿足要求。如果不能,就調(diào)整要求或者提醒用戶游戲桿太落后!設(shè)備的能力可以并且應(yīng)該調(diào)用 IdirectInputDevice 接口的成員函數(shù) GetCapabilities 探測。
這就引出了適用于所有 DirectX 組件的另一個討論點。 DirectX 為多種設(shè)備提供廣泛的支持。軟件開發(fā)環(huán)境和使用環(huán)境可能有很大差別,不同的計算機支持不同水平的 DirectX 功能。編寫好使用 DirectX 的軟件,需要檢查硬件的能力。最差的情況下,如果某個功能不支持,可以退出程序。最好的情況當(dāng)然是程序能夠聰明地根據(jù)缺少的特性調(diào)整本身的需求。
在開始從設(shè)備得到輸入之前,需要設(shè)置設(shè)備的特性。這些特性包括象返回值的范圍、游戲桿的中心點等此類的細(xì)節(jié)。這一工作由函數(shù) SetProperty 完成,相當(dāng)復(fù)雜。
SetProperty 設(shè)置設(shè)備的一個特性。首先,必須使用關(guān)于要改變的設(shè)置的一些信息填寫一個數(shù)據(jù)結(jié)構(gòu)。請參考 Platform SDK 中的文檔,得到所有數(shù)據(jù)結(jié)構(gòu)。每個結(jié)構(gòu)都以一個 DIPROPHEADER 結(jié)構(gòu)開始,此結(jié)構(gòu)中填寫描述要改變的設(shè)置的信息。然后,用特定于所改變的設(shè)置的數(shù)據(jù)填寫結(jié)構(gòu)中剩余的部分。最后,調(diào)用 SetProperty ,參數(shù)是 GUID 和指向結(jié)構(gòu)中 DIPROPHEADER 部分的指針。下面的代碼片段將游戲桿的垂直范圍設(shè)置為 –100 到 100 :
DIPROPRANGE dipRange ;
dipRange.diph.dwSize = sizeof(dipRange);
dipRange.diph.dwHeaderSize = sizeof(dipRange.diph);
dipRange.diph.dwObj = DIJOFS_Y;
dipRange.diph.dwHow = DIPH_BYOFFSET;
dipRange.lMin = -100;
dipRange.lMax = +100;
g_lpDIDeviceJoystick->SetProperty( DIPROP_RANGE, &dipRange.diph) ;
此結(jié)構(gòu)中最難懂的部分是 diph.dwObj 和 diph.dwHow 。 diph.dwHow 描述 diph.dwObj 中保存何種信息。 diph.dwObj 實際描述哪個屬性被設(shè)置。大多數(shù)情況下, diph.dwHow 的值是 DIPH_BYOFFSET , diph.dwObj 的值是傳遞給 SetDataFormat 的結(jié)構(gòu)中一個預(yù)定義的偏移。
應(yīng)該指出能夠列舉設(shè)備的對象,包括按鈕和其它特點。這一工作由 EnumObjects 函數(shù)完成。這樣做時,應(yīng)該提供一個對象標(biāo)志符。將此標(biāo)志符傳遞給 diph.dwObj 成員,將 diph.dwHow 成員填寫為 DIPH_BYID 。
在從設(shè)備讀取數(shù)據(jù)之前,至少要為設(shè)備的 X 和 Y 坐標(biāo)軸設(shè)置最小和最大值。設(shè)置好設(shè)備屬性后,就可以獲得設(shè)備并開始從設(shè)備獲得數(shù)據(jù)。從游戲桿獲取數(shù)據(jù)與從鍵盤或鼠標(biāo)獲取數(shù)據(jù)不同,因為游戲桿是查詢設(shè)備。
鍵盤和鼠標(biāo)會引發(fā)硬件中斷,由系統(tǒng)中的驅(qū)動程序處理,并用來更新通過調(diào)用 GetDeviceState 由 DirectInput 返回的數(shù)據(jù)。查詢設(shè)備(如大多數(shù)游戲桿)不產(chǎn)生硬件中斷,因此, DirectInput 必須被告知從設(shè)備獲取狀態(tài)信息。這一工作通過調(diào)用 IDirectInputDevice2 接口的 Poll 成員函數(shù)完成。此時也是檢查 設(shè)備是否需要重新獲得的適當(dāng)時機。設(shè)備被成功查詢后,就可以調(diào)用 GetDeviceState 獲取狀態(tài)信息。
如果調(diào)用 SetDataFormat 時使用 c_dfDIJoystick 變量,那么 GetDeviceState 將用游戲桿當(dāng)前的狀態(tài)信息填充一個 DIJOYSTATE 結(jié)構(gòu)。此結(jié)構(gòu)的內(nèi)容主要取決于物理設(shè)備的特性和 SetProperty 的設(shè)置。例如,如果結(jié)構(gòu)中的 lY 成員等于 -50 ,并且 Y 軸的范圍設(shè)置為 -100 到 100 ,那么就是說游戲桿在垂直方向上處于中心和最頂端的中間。程序中應(yīng)該確保設(shè)備的范圍設(shè)置為能合理滿足需求的值。為了從游戲桿設(shè)備中獲取數(shù)據(jù),程序應(yīng)該定期查詢設(shè)備。
使用 DirectInputEffect
首先,應(yīng)該解釋一些力反饋技術(shù)。力反饋設(shè)備是能夠產(chǎn)生用戶可以感覺到的力的設(shè)備,這些力叫作效果,例如顛簸效果或者持續(xù)的將操縱桿推向右上方的力。這些效果是“播放”出來的,效果由程序控制播放,或者對函數(shù)調(diào)用響應(yīng),或者對用戶按鍵自動反應(yīng)。
DirectInput 目前支持大約一打不同的效果類型(見表 5 )。這些效果的范圍從完全由程序控制的低級持續(xù)力效果,到由 DirectInput 或設(shè)備自己控制的高級傾斜或波動效果。效果有四種基本類型:持續(xù)力、傾斜效果、周期效果和條件。持續(xù)力是單一方向上不改變強度的力。傾斜效果是強度隨時間線性變化的持續(xù)的力。周期效果是沿著給定的軸重復(fù)變化,其量級或者力的強度由周期效果定義。條件是對用戶與游戲桿的交互作用做出響應(yīng)的效果。這種效果可能是象一根彈簧,操縱桿向某個方向推得越遠(yuǎn),反彈力就越強。
表 5 : DirectInput 效果的類型
|
GUID |
說明 |
使用方法注解 |
|
GUID_ConstantForce |
固定強度、特定方向的持續(xù)拉力。 |
使用 DICONSTANT 力結(jié)構(gòu)作為 DIEFFECT 結(jié)構(gòu)的一部分實現(xiàn)持續(xù)力。 |
|
GUID_CustomForce |
一序列持續(xù)力下傳到設(shè)備,按順序播放。 |
DICUSTOMFORCE 結(jié)構(gòu)被用來定義力。 |
|
GUID_Damper |
隨沿坐標(biāo)軸的移動增加的條件效果。 |
實現(xiàn)這種效果的特定類型結(jié)構(gòu)是 DICONDITION 結(jié)構(gòu)。條件效果通常不支持包。 |
|
GUID_Friction |
阻礙沿坐標(biāo)軸移動的條件效果。 |
實現(xiàn)這種效果的特定類型結(jié)構(gòu)是 DICONDITION 結(jié)構(gòu)。條件效果通常不支持包。 |
|
GUID_Inertia |
隨沿坐標(biāo)軸移動的加速度增加的條件效果。 |
實現(xiàn)這種效果的特定類型結(jié)構(gòu)是 DICONDITION 結(jié)構(gòu)。條件效果通常不支持包。 |
|
GUID_RampForce |
特定方向上大小線性增加或減小的拉力。 |
DIRAMPFORCE 結(jié)構(gòu)被用來作為 DIEFFECT 結(jié)構(gòu)中的類型相關(guān)部分。 |
|
GUID_SawtoothDown |
力瞬間達(dá)到最大然后線性減小到最小的周期效果。 |
需要的特定類型結(jié)構(gòu)是 DIPERIODIC 結(jié)構(gòu)。 |
|
GUID_SawtoothUp |
力從最小線性增加到最大然后瞬間降到最小的周期效果 |
需要的特定類型結(jié)構(gòu)是 DIPERIODIC 結(jié)構(gòu)。 |
|
GUID_Sine |
力正弦變化的周期效果。 |
需要的特定類型結(jié)構(gòu)是 DIPERIODIC 結(jié)構(gòu)。 |
|
GUID_Spring |
力隨到某個中點的相對距離而增大的條件效果。 |
實現(xiàn)這種效果的特定類型結(jié)構(gòu)是 DICONDITION 結(jié)構(gòu)。條件效果通常不支持包。 |
|
GUID_Square |
力瞬時在最大與最小之間轉(zhuǎn)變的周期效果。 |
需要的特定類型結(jié)構(gòu)是 DIPERIODIC 結(jié)構(gòu)。 |
|
GUID_Triangle |
力在最大與最小之間線性變化的周期效果。 |
需要的特定類型結(jié)構(gòu)是 DIPERIODIC 結(jié)構(gòu)。 |
下面所有與力反饋游戲桿有關(guān)的工作都是針對 Microsoft SideWinder Force Feedback Pro 游戲桿,這就是說,本文中的某些細(xì)節(jié)對其它設(shè)備可能多少會產(chǎn)生一些問題。
在創(chuàng)建力反饋效果以前先獲得設(shè)備是一個不錯的想法。雖然這不是必須的,但是在效果能夠被下傳到設(shè)備前必須要獲得設(shè)備。這一點對于播放對用戶按下按鈕做出反應(yīng)的力效果尤其重要。
要創(chuàng)建效果,首先要為每個打算使用的效果創(chuàng)建 DirectInputEffect 對象的實例。這一工作通過調(diào)用 IDirectInputDevice2 接口的 CreateEffect 成員函數(shù)完成。此函數(shù)需要效果的 GUID ,以及指向 DIEFFECT 結(jié)構(gòu)的指針,該結(jié)構(gòu)中填寫的是效果的細(xì)節(jié)。最后, CreateEffect 返回一個指向 IdirectInputEffect 接口的指針,該指針的地址是 CreateEffect 的一個參數(shù)。這個調(diào)用的核心部分集中在 DIEFFECT 結(jié)構(gòu)的填充。
DIEFFECT 結(jié)構(gòu)如下定義:
typedef struct {
DWORD dwSize;
DWORD dwFlags;
DWORD dwDuration;
DWORD dwSamplePeriod;
DWORD dwGain;
DWORD dwTriggerButton;
DWORD dwTriggerRepeatInterval;
DWORD cAxes;
LPDWORD rgdwAxes;
LPLONG rglDirection;
LPDIENVELOPE lpEnvelope;
DWORD cbTypeSpecificParams;
LPVOID lpvTypeSpecificParams;
} DIEFFECT, *LPDIEFFECT;
dwSize 成員是此結(jié)構(gòu)的字節(jié)數(shù)。 DwFlags 指出效果使用的坐標(biāo)類型,以及是使用偏移方法還是 ID 方法描述按鈕(就向前面說明的 SetProperty )。通常情況下,可以設(shè)置為 DIEFF_CARTESIAN|DIEFF_OBJECTOFFSETS ,即按鈕采用偏移描述,坐標(biāo)使用 XYZ 坐標(biāo)形式。
DwDuration 說明效果播放多少毫秒。注意 dwDuration 可以設(shè)為 INFINITE 。 DwSamplePeriod 說明效果播放一個周期花費多少毫秒。不同設(shè)備支持不同的周期。實際中, SideWinder 游戲桿支持的周期不大于 1 秒,不小于 1/80 秒。 DwGain 可以看作效果的主要量,因為它說明效果多么有力。此值的范圍是 0 到 10000 。
DwTriggerButton 和 dwTriggerRepeatInterval 用來設(shè)置觸發(fā)效果播放的按鈕,以及重復(fù)頻率。當(dāng)然,可以通過將 dwTriggerButton 的值設(shè)置為 DIEB_NOTRIGGER 來將效果設(shè)置為與按鈕無關(guān)。否則, dwFlags 定義通過 ID 還是偏移方式描述按鈕。因為偏移方式不需要調(diào)用 EnumObjects ,所以一般可以將值指定為 DIJOFS_ BUTTON0 和 DIJOFS_BUTTON1 。
CAxes 成員說明效果將影響幾個軸。 RgdwAxes 指向一個描述所包含的軸的 DWORD 數(shù)組,數(shù)組中每個軸是一個成員。同按鈕一樣,軸也是用偏移或者 ID 來指明。一般的偏移值包括 DIJOFS_X 和 DIJOFS_Y 。
同樣, rglDirection 成員指向一個 long 型數(shù)組,每個軸是一個成員。在笛卡兒坐標(biāo)中,( Y=-1 , X=1 )與( Y=-10 , X=10 )描述的是同一個方向。這就是說,如果想得到一個不是 45 度整數(shù)倍方向上的斜的力,就應(yīng)該調(diào)整兩個值的相對大小。例如,( Y=-10 , X=1 )描述與上面例子在同一象限的方向,但卻明顯靠近 Y 軸。
效果也可以有描述它們的包。填充一個 DIENVELOPE 結(jié)構(gòu),并將其地址填寫到 lpEnvelope 成員就可以完成。包可以在一段時間內(nèi)影響效果的數(shù)量或力量。其中,起動水平是效果的開始變化點,啟動時間說明效果達(dá)到力量保持階段花費多少毫秒。衰減水平是效果在包最后達(dá)到的水平,衰減時間是衰減用掉了多少豪 秒。包可以用來制造初始狀態(tài)較強,然后慢慢衰減的力效果。圖 1 中描繪了包如何改變效果。
圖 1 :包效果
DIEFFECT 結(jié)構(gòu)的最后兩個成員是 cbTypeSpecificParams 和 lpvTypeSpecificParams 。它們保存特定于所創(chuàng)建效果類型的結(jié)構(gòu)的字節(jié)數(shù)和地址。特定類型的效果使用何種結(jié)構(gòu)的信息見表 5 。
填寫完這個結(jié)構(gòu)并調(diào)用 CreateEffect 后,就會獲得指向 IdirectInputEffect 接口的指針,現(xiàn)在可以使用此接口播放效果,改變效果等。如果沒有將效果聯(lián)系到按鈕,就必須用 IdirectInputEffect 接口的 Start 和 Stop 成員播放和停止效果。如果效果與按鈕關(guān)聯(lián),那么在創(chuàng)建時下傳到設(shè)備;否則,效果在播放時自動下傳到設(shè)備。如果程序必須重新獲得設(shè)備,那么所有與按鈕相關(guān)的效果必須通過明確的調(diào)用 Download 成員才能下傳到設(shè)備。
效果能夠用 Unload 成員卸載,也能夠通過向 SetParameters 成員函數(shù)傳遞新的 DIEFFECT 結(jié)構(gòu)重新設(shè)置參數(shù)。當(dāng)程序用完效果后,必須調(diào)用接口的 Release 成員。
圖 2 :演示程序
首先,應(yīng)該建立演示代碼并運行,應(yīng)該能看到一個游戲桿配置窗口(見圖 2 )。使用游戲桿可以移動中間的人,在窗口的左上角是坐標(biāo)和輸入狀態(tài)信息。如果有力反饋游戲桿,那么通過按下按鈕 1 和 2 應(yīng)該能感覺到一對不同的力。如果將小人撞到窗口的邊緣,應(yīng)該能感到碰撞效果。
這個例子說明了 DirectInput 的使用。這里仍然有相當(dāng)數(shù)量的代碼與 DirectInput 沒有直接關(guān)系。這些代碼根據(jù)功能劃分成模塊。 Main.cpp 是基本的 WinMain 樣板文件和窗口創(chuàng)建代碼。除了調(diào)用初始化函數(shù)外,這部分代碼基本上與本文的其它部分沒有關(guān)系。它創(chuàng)建窗口,進(jìn)入消息循環(huán)。 WndProc.cpp 包含程序窗口的窗口過程。
Demo.cpp 開始了有意義的代碼。不論何時提到“ demo ”,都是指程序游戲。例如, InitDemo 函數(shù)為圖形設(shè)置狀態(tài)數(shù)據(jù)并創(chuàng)建一些所需的時間和線程。除了初始化,此演示程序運行在第二個線程中。該線程嘗試讀取輸入并刷新狀態(tài)數(shù)據(jù),每秒進(jìn)行 32 次。然后使窗口無效,從而讓主線程重新繪制窗口。這就是說,輸入和狀態(tài)變化的一個反復(fù),或者說一個演示周期,大約有 1/32 秒。所以,不管顯示刷新得多么頻繁,輸入響應(yīng)速度都會保持一致。
DX.cpp 包含 DirectX 需要的非常小的初始化和結(jié)束處理,然后調(diào)用完成特殊 DirectInput 工作的函數(shù)。除了 CoInitialize 和 CoUninitialize 外, DXInput 模塊包含本文中提到的所有內(nèi)容。函數(shù)按照演示程序中用到的順序列出,每個只列一次。注意, DirectInput 的大部分工作在初始化中完成。冗長的任務(wù)劃分成幾個函數(shù)列在表 6 中。
表 6 : DXInput.cpp 的函數(shù)
|
成員函數(shù) |
說明 |
|
InitDirectInput |
為系統(tǒng)鍵盤初始化 DirectInput 對象和 DirectInputDevice 對象。 |
|
EnumJoy |
列舉設(shè)備的回調(diào)函數(shù)。此函數(shù)為系統(tǒng)中安裝的第一個游戲桿創(chuàng)建 DirectInputDevice 。 |
|
InitForceFeedback |
如果找到游戲桿是適應(yīng)力反饋的,此函數(shù)就為力反饋效果進(jìn)行一些設(shè)置。 |
|
InitRampEffect, InitBumpEffects, InitWavyEffect |
這些函數(shù)每個都設(shè)置一個效果。這些效果演示了 DirectInput 中幾種不同的效果,并且應(yīng)該對創(chuàng)建新效果有用。 |
這個模塊中的另一個要點是演示程序重復(fù)調(diào)用的函數(shù)。 ForceEffect 播放一個存在的效果, GetKeyboardInput 獲得鍵盤輸入, GetJoystickInput 獲得游戲桿輸入。最后 UnInitDirectInput 結(jié)束所有的一切。
要獲得完整的源代碼,請訪問 MSJ 的 Web 站點 http://www.microsoft.com/msj .
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

