• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            牽著老婆滿街逛

            嚴以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            掌握 DirectX 和 DirectInput ——力反饋游戲桿

            原文地址:http://www.microsoft.com/china/MSDN/library/archives/technic/develop/tsother/0314c.asp

            Jason Clark

            ???? 不知不覺中,Windows下的游戲和多媒體程序已經開始流行。硬件變得越來越快,Windows也變得更加靈活。自從Microsoft發布了DirectX,游戲開發人員對其它平臺已經越來越不感興趣了。許多游戲開發者也已經將他們的開發工作完全移植到了Windows下。

            ???? PC開發游戲從來就沒有輕松過。從無數種顯示卡和聲卡中,開發者學會了在功能性和兼容性之間平衡的藝術。他們不得不處理象頁面切換、段內存結構和位操作這樣令人討厭的問題。并且隨著多人游戲的流行,開發者必須同時處理象網絡和通信等事項。DirectX引入后,游戲開發者變得輕松了。通過為開發者提供的DirectX對象,絕大多數討厭的工作已經被簡化了。

            ???? 基于DirectX的程序是普通的Windows程序嗎?必須懂得COM嗎?為簡單的程序值得使用DirectX嗎?必須使用DirectX的全部組件嗎?這樣的問題肯定還有更多。

            ???? 本文將首先介紹DirectX,然后介紹DirectX的一個組件DirectInput的使用。演示程序說明了DirectInput的用法,著重介紹了其強大的反饋功能。

            DirectX揭密

            ???? DirectX是一套為Windows程序提供對系統硬件更親密控制的組件。(表1列出了DirectX 5.0的組件及其作用)。那么,親密控制是什么意思呢?

            1DirectX 5.0的組件

            組件

            用途

            DirectDraw

            高速2D圖象

            DirectSound

            短響應時間聲音輸出

            Direct3D

            高速3D圖象

            DirectInput

            面向游戲的對游戲桿和其它輸入設備的訪問

            DirectSetup

            方便的安裝DirectX組件

            DirectPlay

            面向游戲的通信和網絡支持

            DirectShow

            視頻流支持

            DirectAnimation

            動畫錄放支持

            ???? DirectX提供的硬件控制常常被描述成底層控制,這會使人聯想起位操作和其它討厭的事情。實際上,DirectX組件包含許多高層API,使得象復制位圖和播放聲音等復雜的工作變得相當簡單。用“為程序提供比過去更好的對硬件的控制”來形容DirectX更準確。這在Windows中是一個顯著的特性,因為在Windows中,資源是共享的,并由操作系統控制。

            ???? DirectX組件遵守稱為COM的二進制對象的工業標準。

            開始DirectX

            ???? 下面從DirectX的安裝開始講起。大多數情況下,某個好玩的游戲就會為系統安裝DirectX。為得到最新的版本,應該從最新的Microsoft Platform SDK中將DirectX安裝到系統中。可以在http://www.microsoft.com/msdn站點或者MSDN光盤中找到platform SDK。缺省情況下,Microsoft Platform SDK被安裝到缺省驅動器根目錄下的\MSSDK目錄中。DirectX的頭文件安裝在\MSSDK\INCLUDE目錄中,Lib文件安裝在\MSSDK\LIB目錄中。

            ???? Platform SDK包含了一些非常好的DirectX例子和文檔。早期發布的DirectX文檔非常粗略而且有些是錯誤的,現在的版本已經極大地改正了這一問題。最好要熟悉這些文檔。

            ???? 現在已經為安裝利用DirectX的程序做好了準備。所幸的是,不必一次就處理DirectX的全部功能。DirectX是一套可以分別使用的組件。實際上,在編程概念中,DirectX的不同部分互相沒有聯系。它們僅僅是具有相同的設計風格和目標:使Windows的游戲編程變得容易。

            ???? 使用DirectX組件的程序有什么特殊的地方嗎?根本沒有。使用DirectX組件的程序是基于Win32的程序,它們使用普通Win32 API集,并且可以訪問所有可以獲得的操作系統工具。實際上,DirectX既可以用于GUI程序,也可以用于控制臺程序。可以直接用Petzold-style SDK編程開發程序,也可以用基本類庫,如MFC。總的說,唯一的要求是大多數DirectX組件在程序中需要HWND,所以至少要有一個窗口。

            ???? 雖然DirectX組件是分離的,但是每個組件的實現風格和使用都是相同的。DirectInput是學習DirectX的非常好的出發點,原因是DirectInput是最簡單的組件之一。

            用力

            ???? 以后在游戲中要“用力”,這是電影《星球大戰》中的說法,因為DirectInput中加入了相當令人陶醉的力反饋支持。DirectX 5.0以前,DirectInput支持從鼠標和鍵盤讀取輸入,這是一個有用但卻令人厭煩的特性。DirectX 5.0中,DirectInput被擴充到支持具有以物理力的形式向用戶傳播反饋的能力的設備。

            ???? 如果不能立即理解上面的內容,下面就用一個游戲進行解釋。假設你剛啟動了你最喜歡的超現實3D越野賽車游戲,正手握力反饋游戲桿。在起跑線上,你可以聽到賽車引擎的空轉聲,同時也能夠通過游戲桿感覺到賽車引擎的空轉!比賽開始后,你可以感覺到引擎高速旋轉的嗡嗡震動。當行駛到賽程中崎嶇的地段時,你將會不停的感覺到電子碰撞。賽車在整個賽場上撞來撞去,你的游戲桿也會如此。賽車車輪卡在車轍中導致賽車被拉向左邊,游戲桿也會被拉向左邊!整個過程中你可以感覺到每次顛簸、刮擦、撞擊和撞毀。

            ???? 現在,帶有支持DirectInputWindows驅動程序的唯一的力反饋設備是MicrosoftSideWinder Force Feedback Pro。這一現狀不會持續太久,新設備以及現有設備的新驅動程序很快就會進入市場。

            剖析DirectInput

            ???? DirectInput由三個對象組成:DirectInput, DirectInputDevice, DirectInputEffect (見表2)DirectInput是一個高層的對象,通過DirectInput對象可以對相關的輸入設備進行基本的初始化和查找。DirectInput對象最終用來創建低層的DirectInputDevice對象。DirectX中的每個主要組件都采用相同的方法,首先創建高層對象,如DirectInputDirectSound對象,然后創建低層對象與硬件進行實際的通信。

            2: DirectInput對象

            對象

            說明

            DirectInput

            封裝高層DirectInput功能,列舉設備并用來創建DirectInputDevice對象。

            DirectInputDevice

            與物理輸入設備的接口,例如游戲桿,包括收集和設置設備狀態信息的接口,并且用來創建DirectInputEffect對象 (對于力反饋設備)

            DirectInputEffect

            封裝能夠在力反饋設備上“播放”的簡單效果,提供啟動、停止和設置力反饋效果等功能。

            ???? DirectInput對象是三個對象中最容易理解的。實際上,它在一個接口形式IDirectInput (見表3)中只提供五個函數。這是DirectInput的一個非常重要的部分,因為這是出發點。

            3IdirectInput接口

            成員函數

            說明

            CreateDevice

            創建一個DirectInputDevice對象并返回一個指向其IdirectInputDevice接口的指針。

            EnumDevices

            為找到的與給定標準匹配的每個設備調用一個回調函數,每個回調函數提供一個GUID,可以用在CreateDevice中創建DirectInputDevice對象。

            GetDeviceStatus

            測試物理設備是否連接到系統。

            Initialize

            如果DirectInput對象是使用CoCreateInstance創建的,那么在使用前必須調用Initialize成員。如果DirectInput對象是使用DirectInputCreate創建的,那么就已經初始化過了。

            RunControlPanel

            為設備運行Windows Control Panel程序,讓用戶安裝新設備或者更改已有設備的配置。游戲桿校準可以在此處做。

            創建DirectInput對象

            ???? 為了創建DirectInput對象并得到其IdirectInput接口指針,應該在程序初始化階段使用兩種方法之一完成。

            ???? 第一種方法相當簡單。DirectX提供了一個助手函數DirectInputCreate來創建并初始化DirectInput對象。它與所有DirectInput的函數、接口和宏定義都在頭文件DINPUT.H中聲明。實際的函數體在DINPUT.LIB文件中。

            DirectInputCreate如下定義:

            HRESULT WINAPI DirectInputCreate(

            ? HINSTANCE hinst,

            ? DWORD dwVersion,

            ? LPDIRECTINPUT * lplpDirectInput,

            ? LPUNKNOWN punkOuter

            );

            ???? 第一個參數是應用程序的實例。第二個參數是程序需要的DirectInput版本,通常使用DIRECTINPUT_VERSION宏,定義為當前版本。第三個參數最重要,如果對COM非常陌生的化就很難理解,它是指向IdirectInput接口的指針的地址。程序中應該定義一個LPDIRECTINPUT類型的變量(可以是全局的)并將其地址作為第三個參數傳遞給DirectInputCreate

            ???? 最后一個參數叫作punkOuter,與COM技術中的聚合有關,可以用NULL安全的忽略。返回值是一個HRESULT,是COM的標準返回類型,可以將返回值與可能的返回值比較,也可以使用COM宏定義SUCCESSFAILED來檢查。

            ???? 使用DirectInputCreate能夠容易地創建高層對象并得到其主接口指針。這是DirectX的又一個設計方法,每個DirectX組件都提供助手函數來創建高層對象,例如DirectInputCreateDirectDrawCreate。在程序中可以用這些助手函數創建DirectX對象,然而,這些函數實際上創建的是COM對象。這個工作也可以用叫作CoCreateInstance的標準Win32 API函數來完成。這就引出了創建DirectInput對象的第二中方法。

            ???? Win32中用CoCreateInstance創建COM對象非常普遍。如果程序中已經使用CoCreateInstance創建了其他COM對象,開發者可能就會希望也用它來創建DirectX對象。因為COM對象在安裝時就在系統中注冊過,所以唯一需要知道的就是對象的GUID,用它來創建一個實例。創建DirectX對象需要的全部GUID都在頭文件中聲明,并在庫文件DXGUID.LIB中定義。可以將一個預定義的GUID傳遞給CoCreateInstance,讓Windows為你創建對象。

            CoCreateInstance定義如下:

            STDAPI CoCreateInstance(

            ? REFCLSID rclsid,

            ? LPUNKNOWN pUnkOuter,

            ? DWORD dwClsContext,

            ? REFIID riid,

            ? LPVOID * ppv

            );

            ??? ?第一個參數是要創建對象的GUIDDirectX定義的GUID是叫作CLSID_DirectInputGUID結構變量。第二個參數是熟悉的pUnkOuter,同樣可以用NULL忽略。第三個參數dwClsContext定義COM對象在何處創建,DirectX只支持進程內服務器,所以必須使用CLSCTX_INPROC_SERVER

            ???? 第四個參數是兩種方法真正的不同之處。記住COM對象對外提供接口,與對象本身一樣,接口也用GUID識別。使用第一種方法,不能選擇得到的接口,總是得到IdirectInput。使用CoCreateInstance可以請求對象所支持的任何接口,方法是使用為接口預定義的GUID。但是在DirectInput這是沒有意義的,因為DirectInput對象的唯一有用的接口就是IdirectInput。其它DirectX組件支持多個有用的接口。(例如,DirectDraw對象可以用IdirectDrawIDirectDraw2接口操作。)

            最后一個參數是程序中接口指針變量的實際地址。

            現在就擁有了對象和對象的一個接口。CoCreateInstance方法還需要另外一步:必須要首先調用一個接口函數初始化對象。DirectInputCreate提供的是一個已經初始化過的DirectInput對象,但CoCreateInstance沒有特定于DirectInput的認識,因此必須調用IdirectInput接口的初始化成員函數。假設如下定義IdirectInput接口指針變量:

            LPDIRECTINPUT g_lpDI

            ???? 可以如下調用初始化函數:

            g_lpDI->Initialize( hInstance, DIRECTINPUT_VERSION);

            ???? 既然選擇采取這種標準方法創建對象,就不得不注意COM需要的其他標準,例如需要調用CoInitializeCoUninitialize

            使用DirectInput對象

            ???? 一旦擁有了DirectInput對象,就可以用它來創建DirectInputDevice對象,來管理系統中特定的設備。創建DirectInputDevice對象要使用CreateDevice函數,它是作為IdirectInput接口一部分的五個函數之一。CreateDevice需要所請求設備的GUID,返回新DirectInputDevice對象的IdirectInputDevice接口指針。

            HRESULT CreateDevice(

            ? REFGUID rguid,

            ? LPDIRECTINPUTDEVICE *lplpDirectInputDevice,

            ? LPUNKNOWN pUnkOuter

            );

            ???? 這些內容看起來很熟悉,因為它與CoCreateInstanceDirectInputCreate類似。但是,現在還沒有完全準備好開始DirectInputDevice對象,原因是在創建DirectInputDevice對象前需要該設備的GUID

            ???? DirectInput庫為創建DirectInputDevice對象預定義了兩個GUIDGUID_SysKeyboardGUID_SysMouse。將兩者之一直接傳遞給CreateDevice函數,就會得到相應設備的DirectInputDevice對象。

            ???? 注意,令人感到奇怪的是缺少對游戲桿的預定義GUID。在Windows中,通常都有系統鍵盤和系統鼠標,另一方面,系統本身并不使用游戲桿。可以安裝一個或者多個游戲桿,但系統管理的范圍只限于驅動程序級。系統并為這些設備指定特殊的系統狀態,也不會在日常事務中使用這些設備。因此,為游戲桿定義GUIDDirectInput來說是不合理的。

            ???? 那么,如何才能找到與系統連接的游戲桿的GUID呢?要得到它們,必須要列舉設備。列舉系統設備和性能在DirectX中相當普遍。要列舉系統中的輸入設備,需要使用EnumDevices函數。EnumDevicesIdirectInput接口的一部分,如下定義:

            HRESULT EnumDevices(

            ? DWORD dwDevType,

            ? LPDIENUMCALLBACK lpCallback,

            ? LPVOID pvRef,

            ? DWORD dwFlags

            );

            ???? 注意此函數與Windows中其它列舉API相同,例如EnumWindows。第二個參數是一個回調函數。第三個參數是程序中定義的32位值。第一個參數是想要列舉的設備類型,對游戲桿來說,是DIDEVTYPE_JOYSTICK(全部的設備類型列在表4中)。最后一個參數是詳細描述想要列舉的設備的標志。現在支持的標志是DIEDFL_ATTACHEDONLYDIEDFL_ALLDEVICES(這兩個標志是互斥獨占的),此外還有DIEDFL_FORCEFEEDBACK,此標志表示力反饋設備,能夠和另兩個標志位或操作。

            4:定義列舉的輸入設備

            ???? 以下定義的值可以傳遞給EnumDevices來選擇列舉哪種類型的輸入設備。另外也支持子類型,見SDKDIDEVICEINSTANCE結構的文檔。

            說明

            DIDEVTYPE_MOUSE

            列舉鼠標設備 (標準、軌跡球等)

            DIDEVTYPE_KEYBOARD

            列舉鍵盤設備 (標準、鍵區等)

            DIDEVTYPE_JOYSTICK

            列舉游戲桿設備 (操縱桿、操縱輪、方向舵等)

            DIDEVTYPE_DEVICE

            列舉其它設備

            ???? EnumDevices列舉系統中的輸入設備時,反復地調用回調函數。回調函數定義如下:

            BOOL CALLBACK EnumProc(LPCDIDEVICEINSTANCE lpddi,LPVOID pvRef) ;

            ???? 因為回調函數是由用戶程序定義并傳遞給EnumDevices的,所以是調用CreateDevice的最合適地方,直到創建了滿足需要的足夠DirectInputDevice對象為止。但是回調函數并非一定要如此實現,可以簡單的將列舉設備的所有GUID保存在一個表中,在以后的代碼中使用。

            ???? 回調函數接受兩個參數。第二個參數是程序定義的傳遞給EnumDevices32位值。更重要的是,第一個參數傳遞指向一個結構的指針,該結構包含關于能夠與列舉標準匹配的單個設備的許多信息。這是一個DIDEVICEINSTANCE結構。此結構中最重要的一條信息是設備的GUID,保存在結構的guidInstance成員中。

            ???? 當程序中完全完成DirectInput有關的工作后,就應該調用IdirectInput接口的Release成員。這就告訴DirectInput對象可以釋放自己了。在DirectX中,最好養成釋放對象的習慣,從低層對象開始,到高層對象結束。正常情況下程序會作為清除或者關閉的例行公事的一部分調用Release。這是使用每個DirectX組件的必要步驟,也是使用每個COM組件的必要步驟。

            ???? 現在已經用CreateDevice成員函數獲得了DirectInputDevice對象的一個接口,為開始處理與系統連接的實際物理設備做好了準備。

            使用DirectInputDevice對象

            ???? DirectInputDevice對象的每個實例都與系統中的特定設備相關。此對象提供了對系統硬件更多的控制和能力,從而使DirectX的允諾實現。下面討論擁有了DirectInputDevice對象后下一步干什么。

            ???? 擁有了IdirectInputDevice接口的一個接口指針,現在干什么?首先,設置設備的數據格式。通過調用SetDataFormat來完成,該函數是一個接口成員函數。設置數據格式包括無數可能的決定,包括軸信息、相對或絕對坐標信息、等等。所有這些細節通過一個叫作DIDATAFORMAT的結構傳遞給此函數。實際上,SetDataFormat唯一的參數就是指向此結構的指針。

            ???? 填寫這個結構的細節會使人發憷。值得感謝的是這一工作并不是必須的,因為DirectInput已經定義了幾個DIDATAFORMAT結構變量,可以用于比較普通的輸入設備:c_dfDIKeyboard, c_dfDIMouse, c_dfDIJoystick, c_dfDIJoystick2。為普通的力反饋游戲桿設置數據格式,可以使用下面的調用形式:

            lpdid->SetDataFormat( &c_dfDIJoystick ) ;

            ???? 在此例中,lpdid是指向IdirectInputDevice接口的指針。

            ???? 設置完設備對象的數據格式后,就需要設置設備的協作級別。因為協作級別在整個DirectX中很常見,所以這里要做一下說明。大多數直接處理系統硬件的DirectX對象在接口的成員中都有一個叫作SetCooperativeLevel函數。這個函數很重要,因為它定義了程序操縱與系統中其它進程有關的硬件的控制級別。同其它DirectX對象一樣,只有設置了協作級別才能使DirectInputDevice對象工作。要理解協作級別,就需要熟悉Acquire函數。調用此函數是為了獲得對物理設備的實際訪問(不要和邏輯上的DirectInputDevice對象混了)。相反的,Unacquire函數釋放對物理設備的訪問。

            ???? 下面是函數SetCooperativeLevel的定義:

            HRESULT SetCooperativeLevel(

            ? HWND hwnd,????

            ? DWORD dwFlags?

            );

            ???? hwnd是程序的主窗口。標志是下面一些值的或操作的結合: DISCL_BACKGROUND, DISCL_FOREGROUND, DISCL_EXCLUSIVE, DISCL_ NONEXCLUSIVE

            ???? 如果標志參數中或上了DISCL_EXCLUSIVE,則當獲得設備后本程序就成為唯一允許訪問該物理設備的進程。另一方面,如果選擇了DISCL_NONEXCLUSIVE,那么系統中可以有多個進程同時協作獲得和使用該設備。如果或上了DISCL_BACKGROUND,程序將不會失去物理設備。然而,象Ctrl+Alt+Del組合鍵被按下這樣的系統事件仍然能夠隱含地“unacquire”程序中的設備。如果使用了DISCL_ FOREGROUND,當不是活動窗口時,程序將會自動釋放物理設備。這就是將程序主窗口句柄傳遞給SetCooperativeLevel的意義。DirectX根據窗口是否是系統當前活動窗口自動調整設備共享。

            ???? 那么所有這些值的意義是什么呢?下面舉個例子說明。如果力反饋游戲桿的協作模式是DISCL_FOREGROUND | DISCL_EXCLUSIVE,那么只要程序處于活動狀態,就能夠從游戲桿讀數據并播放力反饋效果(力反饋需要exclusive-level協作)。只要用戶一選擇其它程序,程序就失去對物理設備的控制,新激活的程序就能夠訪問該設備。這意味著在調試程序時,如果切換到調試器窗口,程序就會因為窗口變為非活動的而失去對游戲桿的控制。

            ???? 如果將同一游戲桿的協作級別設為DISCL_BACKGROUND | DISCL_EXCLUSIVE將會是什么情況呢?程序將會所有時間都能訪問游戲桿,不管窗口的狀態。但是現在系統中其它進程就不能獲得游戲桿,除非程序釋放了游戲桿,不管用戶在做什么!

            ??? 非常明顯,在正式發布的產品中應該使用DISCL_FOREGROUND | DISCL_EXCLUSIVE,而在調試版本中應該使用DISCL_BACKGROUND|DISCL_EXCLUSIVE。但是也不總是這樣選擇。例如,如果設備是系統鍵盤,那么DirectInputDevice想獨占使用而調用SetCooperativeLevel將會失敗。這是因為操作系統想要允許用戶自由地從一個程序切換到另一個程序。類似的,DirectInputDevice不會允許以協作級別DISCL_BACKGROUND|DISCL_EXCLUSIVE請求系統鼠標。Windows不希望一個程序能夠完全將用戶與操作系統的聯系切斷。

            ???? 在能夠從物理設備讀取信息或向物理設備發送信息之前,必須要用Acquire獲得設備。在臨時或永久結束設備使用時要明確地使用Unacquire函數釋放設備。但Unacquire并不是失去設備控制的唯一方法。

            ???? 如果設置協作級別時使用DISCL_FOREGROUND標志,那么程序的主窗口不再是系統中的活動窗口時設備將被明確釋放。這就是說,在程序調用Acquire和實際試圖從設備讀取信息之間,能夠失去對設備的占有。所以需要檢查返回值來捕捉這樣的錯誤,并準備好在任何時間重新獲得該設備。

            ???? 關于AcquireUnacquire的決定性要點:當程序獲得獨占協作級別的設備時,DirectX擁有該設備。例如,如果鼠標被DirectX(獨占)獲得,那么程序窗口中的按鈕就不會對鼠標做出響應。這就是說,如果想讓Windows對設備響應,就應該釋放該設備。換句話說,如果不想讓DirectInput從設備中讀取數據,就調用Unacquire

            ???? 設置完設備的協作級別后,接著應該為設備配置其它設置。獲得了設備后,接著就應該開始使用GetDeviceState函數輪流檢測輸入的數據。當完成與設備對象的操作后,調用Unacquire釋放DirectInputDevice對象。設備與設備之間存在細節上的差別;下面講解游戲桿和鍵盤,應該能為從其它設備讀取輸入提供足夠的基礎知識。

            鍵盤

            ???? 鍵盤是到目前為止最容易讀取的設備。實際上,設置完數據格式、協作級別、獲得設備以后,就可以讀取鍵盤狀態了。讀取鍵盤狀態要使用IdirectInputDevice接口的GetDeviceState成員。GetDeviceState用關于物理設備的狀態信息組裝一個結構,所組裝結構的類型由前面對SetDataFormat的調用決定。對鍵盤來說,此數據結構是一個簡單的256個字節組成的數組。每個字節對應于鍵盤上的一個鍵,如果某個鍵按下,相應字節的高位就被設置。

            ???? DirectInput定義了一套以DIK_XXX為前綴的常量,這些常量可以用來索引字節數組以找到關于特定鍵的數據。例如,如果要檢查右Shif鍵當前是否按下,可以使用DIK_RSHIFT定義:

            GetDeviceState(256,(LPVOID) cKeyboardData) ;

            if(cKeyboardData[DIK_ RSHIFT]&0x80)

            ??? DoWhatever() ;

            ???? CKeyboardData256個字節的緩沖區。幾乎就是這么簡單,但是要記住,不管GetDeviceState在何時返回DIERR_INPUTLOST,就必須使用Acquire獲得設備。這種情況發生在每次用戶從程序切換離開的時候。

            ???? 還有一點很重要,就是能夠請求DirectInput緩沖鍵盤信息。這要求提供一個緩沖區并使用SetProperty為設備設置緩沖區大小。在本文中沒有篇幅討論這一技術,但這一技術在程序不能相當頻繁的檢查鍵盤狀態時非常有用。用戶有可能在程序中兩次GetDeviceState調用之間按下又松開了一個鍵,如果DirectInput不緩沖鍵盤數據的化,這種擊鍵動作就丟失了。

            游戲桿

            ??? 游戲桿非常好玩。與其好聽的名稱(Joystick——原意為歡樂桿)相符,這種設備為游戲體驗添加了許多樂趣,同時也為程序員的體驗添加了一些東西。正常情況下,通過調用IdirectInput接口的CreateDevice成員得到IdirectInputDevice接口(和對象),這對游戲桿也適用。

            ??? ?但是開發人員都希望立即將接口升級到IDirectInputDevice2,那么可以象下面這樣使用QueryInterface調用請求CreateDevice返回新的接口:

            hr = lpDIDeviceJoystickTemp->QueryInterface(???? IID_IDirectInputDevice2,

            ??? (void **) &g_lpDIDeviceJoystick);

            ???? 如果成功,就可以釋放原來的接口,開始使用漂亮的新IDirectInputDevice2接口。但是為什么要這么做呢?IDirectInputDevice2接口提供IdirectInputDevice的所有功能,而且還有另外兩個重要特性:支持查詢設備和支持力反饋設備。

            ???? 其次,需要設置上的一些考慮。還記得SetDataFormat定義了GetDeviceState返回的數據的類型。對于游戲桿設備,使用c_dfDIJoystickc_dfDIJoystick2兩個預定義變量之一,將返回數據的類型設置為DIJOYSTATEDIJOYSTATE2結構。選擇哪種主要取決于要使用游戲桿哪種類型的特性。瀏覽這些結構中的成員應該對弄清這個問題有幫助。

            ???? 同所有輸入設備一樣,要為游戲桿設置數據格式和協作級別。游戲桿往往比鍵盤需要更多一點注意。這是因為現在還幾乎沒有功能完美的游戲桿,所以程序應該檢查以確保控制的設備能滿足要求。如果不能,就調整要求或者提醒用戶游戲桿太落后!設備的能力可以并且應該調用IdirectInputDevice接口的成員函數GetCapabilities探測。

            ???? 這就引出了適用于所有DirectX組件的另一個討論點。DirectX為多種設備提供廣泛的支持。軟件開發環境和使用環境可能有很大差別,不同的計算機支持不同水平的DirectX功能。編寫好使用DirectX的軟件,需要檢查硬件的能力。最差的情況下,如果某個功能不支持,可以退出程序。最好的情況當然是程序能夠聰明地根據缺少的特性調整本身的需求。

            ???? 在開始從設備得到輸入之前,需要設置設備的特性。這些特性包括象返回值的范圍、游戲桿的中心點等此類的細節。這一工作由函數SetProperty完成,相當復雜。

            ???? SetProperty設置設備的一個特性。首先,必須使用關于要改變的設置的一些信息填寫一個數據結構。請參考Platform SDK中的文檔,得到所有數據結構。每個結構都以一個DIPROPHEADER結構開始,此結構中填寫描述要改變的設置的信息。然后,用特定于所改變的設置的數據填寫結構中剩余的部分。最后,調用SetProperty,參數是GUID和指向結構中DIPROPHEADER部分的指針。下面的代碼片段將游戲桿的垂直范圍設置為–100100

            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) ;

            ???? 此結構中最難懂的部分是diph.dwObjdiph.dwHowdiph.dwHow描述diph.dwObj中保存何種信息。diph.dwObj實際描述哪個屬性被設置。大多數情況下,diph.dwHow的值是DIPH_BYOFFSETdiph.dwObj的值是傳遞給SetDataFormat的結構中一個預定義的偏移。

            ???? 應該指出能夠列舉設備的對象,包括按鈕和其它特點。這一工作由EnumObjects函數完成。這樣做時,應該提供一個對象標志符。將此標志符傳遞給diph.dwObj成員,將diph.dwHow成員填寫為DIPH_BYID

            ???? 在從設備讀取數據之前,至少要為設備的XY坐標軸設置最小和最大值。設置好設備屬性后,就可以獲得設備并開始從設備獲得數據。從游戲桿獲取數據與從鍵盤或鼠標獲取數據不同,因為游戲桿是查詢設備。

            ???? 鍵盤和鼠標會引發硬件中斷,由系統中的驅動程序處理,并用來更新通過調用GetDeviceStateDirectInput返回的數據。查詢設備(如大多數游戲桿)不產生硬件中斷,因此,DirectInput必須被告知從設備獲取狀態信息。這一工作通過調用IDirectInputDevice2接口的Poll成員函數完成。此時也是檢查???? 設備是否需要重新獲得的適當時機。設備被成功查詢后,就可以調用GetDeviceState獲取狀態信息。

            ???? 如果調用SetDataFormat時使用c_dfDIJoystick變量,那么GetDeviceState將用游戲桿當前的狀態信息填充一個DIJOYSTATE結構。此結構的內容主要取決于物理設備的特性和SetProperty的設置。例如,如果結構中的lY成員等于-50,并且Y軸的范圍設置為-100100,那么就是說游戲桿在垂直方向上處于中心和最頂端的中間。程序中應該確保設備的范圍設置為能合理滿足需求的值。為了從游戲桿設備中獲取數據,程序應該定期查詢設備。

            使用DirectInputEffect

            ??? 首先,應該解釋一些力反饋技術。力反饋設備是能夠產生用戶可以感覺到的力的設備,這些力叫作效果,例如顛簸效果或者持續的將操縱桿推向右上方的力。這些效果是“播放”出來的,效果由程序控制播放,或者對函數調用響應,或者對用戶按鍵自動反應。

            ???? DirectInput目前支持大約一打不同的效果類型(見表5)。這些效果的范圍從完全由程序控制的低級持續力效果,到由DirectInput或設備自己控制的高級傾斜或波動效果。效果有四種基本類型:持續力、傾斜效果、周期效果和條件。持續力是單一方向上不改變強度的力。傾斜效果是強度隨時間線性變化的持續的力。周期效果是沿著給定的軸重復變化,其量級或者力的強度由周期效果定義。條件是對用戶與游戲桿的交互作用做出響應的效果。這種效果可能是象一根彈簧,操縱桿向某個方向推得越遠,反彈力就越強。

            5DirectInput效果的類型

            GUID

            說明

            使用方法注解

            GUID_ConstantForce

            固定強度、特定方向的持續拉力。

            使用DICONSTANT力結構作為DIEFFECT結構的一部分實現持續力。

            GUID_CustomForce

            一序列持續力下傳到設備,按順序播放。

            DICUSTOMFORCE結構被用來定義力。

            GUID_Damper

            隨沿坐標軸的移動增加的條件效果。

            實現這種效果的特定類型結構是DICONDITION結構。條件效果通常不支持包。

            GUID_Friction

            阻礙沿坐標軸移動的條件效果。

            實現這種效果的特定類型結構是DICONDITION結構。條件效果通常不支持包。

            GUID_Inertia

            隨沿坐標軸移動的加速度增加的條件效果。

            實現這種效果的特定類型結構是DICONDITION結構。條件效果通常不支持包。

            GUID_RampForce

            特定方向上大小線性增加或減小的拉力。

            DIRAMPFORCE結構被用來作為DIEFFECT結構中的類型相關部分。

            GUID_SawtoothDown

            力瞬間達到最大然后線性減小到最小的周期效果。

            需要的特定類型結構是DIPERIODIC結構。

            GUID_SawtoothUp

            力從最小線性增加到最大然后瞬間降到最小的周期效果

            需要的特定類型結構是DIPERIODIC結構。

            GUID_Sine

            力正弦變化的周期效果。

            需要的特定類型結構是DIPERIODIC結構。

            GUID_Spring

            力隨到某個中點的相對距離而增大的條件效果。

            實現這種效果的特定類型結構是DICONDITION結構。條件效果通常不支持包。

            GUID_Square

            力瞬時在最大與最小之間轉變的周期效果。

            需要的特定類型結構是DIPERIODIC結構。

            GUID_Triangle

            力在最大與最小之間線性變化的周期效果。

            需要的特定類型結構是DIPERIODIC結構。

            ???? 下面所有與力反饋游戲桿有關的工作都是針對Microsoft SideWinder Force Feedback Pro游戲桿,這就是說,本文中的某些細節對其它設備可能多少會產生一些問題。

            ???? 在創建力反饋效果以前先獲得設備是一個不錯的想法。雖然這不是必須的,但是在效果能夠被下傳到設備前必須要獲得設備。這一點對于播放對用戶按下按鈕做出反應的力效果尤其重要。

            ???? 要創建效果,首先要為每個打算使用的效果創建DirectInputEffect對象的實例。這一工作通過調用IDirectInputDevice2接口的CreateEffect成員函數完成。此函數需要效果的GUID,以及指向DIEFFECT結構的指針,該結構中填寫的是效果的細節。最后,CreateEffect返回一個指向IdirectInputEffect接口的指針,該指針的地址是CreateEffect的一個參數。這個調用的核心部分集中在DIEFFECT結構的填充。

            ???? DIEFFECT結構如下定義:

            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成員是此結構的字節數。DwFlags指出效果使用的坐標類型,以及是使用偏移方法還是ID方法描述按鈕(就向前面說明的SetProperty)。通常情況下,可以設置為DIEFF_CARTESIAN|DIEFF_OBJECTOFFSETS,即按鈕采用偏移描述,坐標使用XYZ坐標形式。

            DwDuration說明效果播放多少毫秒。注意dwDuration可以設為INFINITEDwSamplePeriod說明效果播放一個周期花費多少毫秒。不同設備支持不同的周期。實際中,SideWinder游戲桿支持的周期不大于1秒,不小于1/80秒。DwGain可以看作效果的主要量,因為它說明效果多么有力。此值的范圍是010000

            DwTriggerButtondwTriggerRepeatInterval用來設置觸發效果播放的按鈕,以及重復頻率。當然,可以通過將dwTriggerButton的值設置為DIEB_NOTRIGGER來將效果設置為與按鈕無關。否則,dwFlags定義通過ID還是偏移方式描述按鈕。因為偏移方式不需要調用EnumObjects,所以一般可以將值指定為DIJOFS_ BUTTON0DIJOFS_BUTTON1

            CAxes成員說明效果將影響幾個軸。RgdwAxes指向一個描述所包含的軸的DWORD數組,數組中每個軸是一個成員。同按鈕一樣,軸也是用偏移或者ID來指明。一般的偏移值包括DIJOFS_XDIJOFS_Y

            ???? 同樣,rglDirection成員指向一個long型數組,每個軸是一個成員。在笛卡兒坐標中,(Y=-1X=1)與(Y=-10X=10)描述的是同一個方向。這就是說,如果想得到一個不是45度整數倍方向上的斜的力,就應該調整兩個值的相對大小。例如,(Y=-10X=1)描述與上面例子在同一象限的方向,但卻明顯靠近Y軸。

            ???? 效果也可以有描述它們的包。填充一個DIENVELOPE結構,并將其地址填寫到lpEnvelope成員就可以完成。包可以在一段時間內影響效果的數量或力量。其中,起動水平是效果的開始變化點,啟動時間說明效果達到力量保持階段花費多少毫秒。衰減水平是效果在包最后達到的水平,衰減時間是衰減用掉了多少豪秒。包可以用來制造初始狀態較強,然后慢慢衰減的力效果。圖1中描繪了包如何改變效果。

            1:包效果

            ???? DIEFFECT結構的最后兩個成員是cbTypeSpecificParamslpvTypeSpecificParams。它們保存特定于所創建效果類型的結構的字節數和地址。特定類型的效果使用何種結構的信息見表5

            ???? 填寫完這個結構并調用CreateEffect后,就會獲得指向IdirectInputEffect接口的指針,現在可以使用此接口播放效果,改變效果等。如果沒有將效果聯系到按鈕,就必須用IdirectInputEffect接口的StartStop成員播放和停止效果。如果效果與按鈕關聯,那么在創建時下傳到設備;否則,效果在播放時自動下傳到設備。如果程序必須重新獲得設備,那么所有與按鈕相關的效果必須通過明確的調用Download成員才能下傳到設備。

            ???? 效果能夠用Unload成員卸載,也能夠通過向SetParameters成員函數傳遞新的DIEFFECT結構重新設置參數。當程序用完效果后,必須調用接口的Release成員。

            演示例子

            ??

            2:演示程序

            ???? 首先,應該建立演示代碼并運行,應該能看到一個游戲桿配置窗口(見圖2)。使用游戲桿可以移動中間的人,在窗口的左上角是坐標和輸入狀態信息。如果有力反饋游戲桿,那么通過按下按鈕12應該能感覺到一對不同的力。如果將小人撞到窗口的邊緣,應該能感到碰撞效果。

            ???? 這個例子說明了DirectInput的使用。這里仍然有相當數量的代碼與DirectInput沒有直接關系。這些代碼根據功能劃分成模塊。Main.cpp是基本的WinMain樣板文件和窗口創建代碼。除了調用初始化函數外,這部分代碼基本上與本文的其它部分沒有關系。它創建窗口,進入消息循環。WndProc.cpp包含程序窗口的窗口過程。

            ???? Demo.cpp開始了有意義的代碼。不論何時提到“demo”,都是指程序游戲。例如,InitDemo函數為圖形設置狀態數據并創建一些所需的時間和線程。除了初始化,此演示程序運行在第二個線程中。該線程嘗試讀取輸入并刷新狀態數據,每秒進行32次。然后使窗口無效,從而讓主線程重新繪制窗口。這就是說,輸入和狀態變化的一個反復,或者說一個演示周期,大約有1/32秒。所以,不管顯示刷新得多么頻繁,輸入響應速度都會保持一致。

            ???? DX.cpp包含DirectX需要的非常小的初始化和結束處理,然后調用完成特殊DirectInput工作的函數。除了CoInitializeCoUninitialize外,DXInput模塊包含本文中提到的所有內容。函數按照演示程序中用到的順序列出,每個只列一次。注意,DirectInput的大部分工作在初始化中完成。冗長的任務劃分成幾個函數列在表6中。

            6DXInput.cpp的函數

            成員函數

            說明

            InitDirectInput

            為系統鍵盤初始化DirectInput對象和DirectInputDevice對象。

            EnumJoy

            列舉設備的回調函數。此函數為系統中安裝的第一個游戲桿創建DirectInputDevice

            InitForceFeedback

            如果找到游戲桿是適應力反饋的,此函數就為力反饋效果進行一些設置。

            InitRampEffect, InitBumpEffects, InitWavyEffect

            這些函數每個都設置一個效果。這些效果演示了DirectInput中幾種不同的效果,并且應該對創建新效果有用。

            ???? 這個模塊中的另一個要點是演示程序重復調用的函數。ForceEffect播放一個存在的效果,GetKeyboardInput獲得鍵盤輸入,GetJoystickInput獲得游戲桿輸入。最后UnInitDirectInput結束所有的一切。

            ???? 要獲得完整的源代碼,請訪問MSJWeb站點http://www.microsoft.com/msj.

            posted on 2006-06-20 15:27 楊粼波 閱讀(1307) 評論(0)  編輯 收藏 引用 所屬分類: 文章收藏

            久久综合给合久久狠狠狠97色69| 久久久WWW成人| 美女久久久久久| 99久久国产热无码精品免费久久久久| 老色鬼久久亚洲AV综合| 久久国产色av免费看| 亚洲精品无码成人片久久| 久久国产精品无| 无码8090精品久久一区| 亚洲国产成人精品无码久久久久久综合 | 国内精品九九久久久精品| 97久久婷婷五月综合色d啪蜜芽| 欧美精品乱码99久久蜜桃| 亚洲а∨天堂久久精品| 蜜臀久久99精品久久久久久| 久久久精品久久久久特色影视| 九九热久久免费视频| 久久无码国产| 亚洲精品乱码久久久久66| 久久w5ww成w人免费| 91麻豆精品国产91久久久久久| 狠狠久久综合伊人不卡| 久久亚洲国产成人影院| 亚洲国产欧美国产综合久久| 国产成人久久精品区一区二区| 99久久免费国产精品| 一本色综合久久| 久久婷婷五月综合色高清 | 久久久久亚洲AV成人网人人网站| 久久久久国产精品人妻| 99久久精品国产高清一区二区| 久久99精品久久久久久不卡| 久久精品中文无码资源站| 久久A级毛片免费观看| 欧美色综合久久久久久| 欧美熟妇另类久久久久久不卡 | 久久久不卡国产精品一区二区 | 久久精品夜夜夜夜夜久久| 久久精品国产欧美日韩| 久久久噜噜噜久久熟女AA片| 久久久久免费视频|