我想關(guān)于這個主題的文章,不算少,但也不算太多。但大多是分別介紹 DirectDraw 與 DirectInput,而并沒有將其結(jié)合起來,也許你會問:“分開與合并起來并沒有本質(zhì)區(qū)別啊!”。其實的確沒有本質(zhì)區(qū)別,但那樣使那些最初對游戲編程報有極大熱情的愛好者感到非常失望,因為這其中的一個并不能完全滿足他們的要求,并且使其感到巨大的阻力,從而失去信心。所以本文將 DirectDraw 與 DirectInput結(jié)合起來去講一個主題就是“游戲編程”,請注意是“游戲編程”,當(dāng)然這只是一個簡單的桌面游戲,但這已經(jīng)與先前有很大的不同了,這已不是簡單的 DirectDraw或 DirectInput編程。我想你現(xiàn)在應(yīng)該能夠體會出其中的區(qū)別了。 聲明:在這之前需要你具有一定的 WIN32 API 函數(shù)的知識,并且可以熟練使用。和 DirectDraw的知識,關(guān)于DirectDraw可以參見 www.frontfree.net 中的 <<動畫程序編寫——DirectDraw之旅>> 1-3),或其它文章。最后是 c++ 語言,當(dāng)然也要包括面向?qū)ο蟮哪遣糠帧T?Visual C++ .NET 編譯環(huán)境下進(jìn)行開發(fā)的。 首先 ,我們還是先簡要復(fù)習(xí)一下DirectDraw的概念吧! DirectDraw本質(zhì)上是顯存管理程序。它最重要的性能是允許程序員直接在顯存里存儲和操縱位圖。它使你能夠利用視頻硬件bliter(位塊傳輸器)在顯存內(nèi)部進(jìn)行位圖的blit(位塊傳輸)。用視頻硬件的blitter從顯存向顯存進(jìn)行blit比從內(nèi)存向顯存更快。這在64位顯卡向顯存提供64位數(shù)據(jù)路徑的今天顯得尤其重要,硬件獨立于促CPU進(jìn)行位塊傳輸操作,使得CPU得以繼續(xù)工作。另外DirectDraw支持顯卡的其他硬件加速特性,例如對精靈和z -buffering的硬件支持。 DirectDraw的工作原理 我們這里還是用圖表方式展現(xiàn)給大家吧! 
細(xì)心的朋友可以很明顯地注意到圖示中的右上角的圖解中說明,表面對象有兩個寬度,一個是WIDTH,一個是PITCH。WIDTH就是創(chuàng)建表面時所給出的那個寬度,而PITCH是表面的實際寬度,是按字節(jié)算的。在許多顯卡上,PITCH和WIDTH是相等的,比如在640x480的高彩模式下,PITCH為1280。而在某些顯卡上,PITCH比WIDTH要大。比如在640x480的256色模式下,當(dāng)WIDTH是640時,PITCH為1024而不是640,這些顯卡這樣做是為了更好地進(jìn)行數(shù)據(jù)對齊來提高性能或達(dá)到其它目的。所以,我們在實際編程時,為了保證程序的兼容性,必須按PITCH處理。 但這些硬件的底層問題,我們不用太關(guān)心,只要稍有了解就可以了。 下面我們再簡要敘述一下,如何使用 DirectX 9.0 中提供的 DirectDraw 類庫來創(chuàng)建對象并使用操作對象。 宏定義在先,定義刪除指針和釋放對象的宏 #defineSAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } |
先創(chuàng)建一個 CDisplay 的全局對象 CDisplay就是ddutil.h中定義的類,用于處理表面之間的拷貝翻頁等操作的類,再次定義一個全局變量,用于以后對指向的表面之間進(jìn)行操作 CDisplay* g_pDisplay = NULL; |
然后創(chuàng)建表面,當(dāng)然可以創(chuàng)建很多的表面,這些表面都是離屏表面,在更新畫面時,都可以用 CDisplay 類的對象中的方法,將其拷貝到后備緩沖區(qū)表面上。只要創(chuàng)建離屏表面,就要用到 CSurface 類。CSurface也是ddutil.h頭文件中定義的類,用于對表面本身進(jìn)行操作,如設(shè)置色彩鍵碼,在此定義的圖畫指針。 CSurface* g_pBackSurface = NULL; |
DirectX 中就一共用這兩個類封裝了 DirectDraw 對象的大部分操作,如果你覺得這還不能滿足要求,那么你也可以在程序中用 DirectDraw API 函數(shù)編寫程序,不過在本文中不再介紹。 這之后,我們會用到 InitDirectDraw 函數(shù)。這個函數(shù)是我們自己創(chuàng)建的。在此函數(shù)中作所有的 DirectDraw 的對象初始化工作。 HRESULT InitDirectDraw( HWND hWnd ) { ?????HRESULT hr; //接受返回值,其實是long型變量 ?????LPDIRECTDRAWPALETTE pDDPal = NULL; //定義程序中的調(diào)色板 ?????int iSprite;//定義與sprite個數(shù)有關(guān)的計數(shù)器 ?????g_pDisplay = newCDisplay();//動態(tài)開辟一個CDisplay類 ?????if( FAILED( hr = g_pDisplay->CreateFullScreenDisplay( hWnd, SCREEN_WIDTH, ???????????SCREEN_HEIGHT, SCREEN_BPP ) ) )/*設(shè)置程序為全屏,并且 g_pDisplay 就是動態(tài)開辟一個CDisplay類的指針,而在這個類的域中,有一個DirectDraw主表面指針,和一個后備緩沖區(qū)表面的指針。在從我建議你可以先去閱讀一下 ddutil.h 和 ddutil.cpp 文件。*/ ?????{ ???????????MessageBox( hWnd, TEXT("This display card does not support 1024x768x8. "), ?????????????????TEXT("DirectDraw Sample"), MB_ICONERROR | MB_OK ); ???????????return hr; ?????} ?????if( FAILED( hr = g_pDisplay->CreatePaletteFromBitmap( &pDDPal, MAKEINTRESOURCE( IDB_DIRECTX ) ) ) )//顧名思義,就是從bmp圖片中獲得調(diào)色板值,并賦值在pDDPal結(jié)構(gòu)指針?biāo)赶虻慕Y(jié)構(gòu)體中。 ???????????returnhr; ?????if( FAILED( hr = g_pDisplay->SetPalette( pDDPal ) ) )//用剛才從IDB_DIRECTX中獲得的調(diào)色板制來設(shè)置程序調(diào)色板 ???????????returnhr; ?????SAFE_RELEASE( pDDPal );//釋放指針,在用過后,一定要釋放,這是良好的編程習(xí)慣 ??????// 用IDB_WINXP圖片創(chuàng)建一個表面,并用g_pBackSurface指向這個表面 ?????if( FAILED( hr = g_pDisplay->CreateSurfaceFromBitmap( ??????&g_pBackSurface, MAKEINTRESOURCE( IDB_WINXP ), ??????SCREEN_WIDTH, SCREEN_HEIGHT ) ) ) ???????????return hr;//設(shè)置色彩鍵碼為黑色,0代表黑色,這樣在表面的拷貝過程中黑色像素的點將不會被拷貝,這樣可以產(chǎn)生鏤空效果。當(dāng)然你可以任意設(shè)置關(guān)鍵顏色,而顏色的表示法可以用 RGB 宏定義。例如 紅色:RGB( 255,0,0 ), 黑色 RGB( 255,255,255 ) ??????if( FAILED( hr = g_pBackSurface->SetColorKey( RGB( 255,255,255 ) ) ) ) ???????????returnhr; ?????returnS_OK; } |
下面的函數(shù)是用于更新畫面的。 HRESULT DisplayFrame() { ?????HRESULT hr; ?????g_pDisplay->Clear( 0 ); //清空后備緩沖區(qū)表面 ?????//將g_pBackSurface所指向的圖片拷貝到后備緩沖區(qū)表面 ??????g_pDisplay->Blt( 0, 0, g_pBackSurface, NULL );//最關(guān)鍵的地方在這里,請看下面的語句,只要我們一執(zhí)行翻頁操作,就可以將改動了的圖像了顯示在屏幕上了 ?????if( FAILED( hr = g_pDisplay->Present()/*翻頁操作*/) ) ???????????returnhr; ?????returnS_OK; } |
下面的函數(shù)是用于在程序失去焦點時調(diào)用的。 HRESULT RestoreSurfaces() { ?????HRESULT hr; ?????LPDIRECTDRAWPALETTE pDDPal = NULL;/*當(dāng)程序失去焦點,要保存當(dāng)前的畫面,請注意這里,g_pDisplay->GetDirectDraw()函數(shù)返回的才是真正的 DirectDraw 對象 */ ?????if( FAILED( hr = g_pDisplay->GetDirectDraw()->RestoreAllSurfaces() ) ) ???????????returnhr;//在此我們還要重新創(chuàng)建調(diào)色板 ?????if( FAILED( hr = g_pDisplay->CreatePaletteFromBitmap( &pDDPal, MAKEINTRESOURCE( IDB_DIRECTX ) ) ) ) ???????????returnhr;//重新設(shè)置調(diào)色板 ?????if( FAILED( hr = g_pDisplay->SetPalette( pDDPal ) ) ) ???????????returnhr; ?????SAFE_RELEASE( pDDPal );//重新畫出圖畫 ?????if( FAILED( hr = g_pLogoSurface->DrawBitmap( MAKEINTRESOURCE( IDB_WINXP ), SPRITE_DIAMETER, SPRITE_DIAMETER ) ) ) ???????????returnhr; ?????returnS_OK; } |
下面這個函數(shù)是釋放表面指針?biāo)玫摹?/p> VOID FreeDirectDraw() { ??????SAFE_DELETE( g_pBackSurface ); ??????SAFE_DELETE( g_pDisplay ); } |
我們的回顧到此結(jié)束,下面我們開始本文要介紹的一個關(guān)鍵技術(shù),DirectInput 的使用。 游戲編程可不僅僅是圖形程序的開發(fā)工作,實際上包含了許多方面,本文所要講述的就是關(guān)于如何使用 DirectInput 來對鍵盤編程的問題。 而我們?yōu)槭裁匆x擇用 DirectInput 來處理游戲中的輸入問題呢?其實用 Win32 API 函數(shù)也完全可以處理這些工作,例如其中,有一個 GetAsyncKeyState() 的函數(shù)可以返回一個指定鍵的當(dāng)前狀態(tài)是按下還是松開。這個函數(shù)還能返回該指定鍵在上次調(diào)用 GetAsyncKeyState() 函數(shù)以后,是否被按下過。雖然這個函數(shù)聽上去很不錯,但需要我們自己輪換查詢每個鍵盤的狀態(tài)。而在 DirectInput 中我們已經(jīng)可以脫離這些煩瑣的工作,只因它的功能更強大。 由于本文重點在二者的結(jié)合,故在此只介紹 DirectInput 中比較簡單的,而且最容易上手的立即模式的工作方式。 而這里我們要用到 DirectInput 的 API 函數(shù)。有人會問,為什么在 DirectDraw 中用 DirectX 提供的類庫編程,而對于 DirectInput 卻直接使用要用其 API 函數(shù)呢,是因為沒有提供 DirectInput 的類庫嗎?不是!而是因為使用類庫并不很方便而且不靈活。 OK,讓我們開始我們游戲編程的第二部——DirectInput編程。 前面講 DirectDraw 時,并沒有提到,微軟是按 COM 來設(shè)計DirectX的,所以就有了一個 DIRECTINPUT 對象來表示輸入設(shè)備,而某個具體的設(shè)備由 DIRECTINPUTDEVICE 對象來表示。也許會感到很無奈,怎么游戲編程需要這么多的知識啊,其實您也無需煩惱,只要知道一下就可以了,其實這并不;影響您的設(shè)計,而且就算您不知道,也同樣可以駕馭DIRECTINPUT。 實際的建立過程是先創(chuàng)建一個 DIRECTINPUT 對象,然后在通過此對象的 CreateDevice 方法來創(chuàng)建 DIRECTINPUTDEVICE 對象。 #include <dinput.h> #defineDINPUT_BUFFERSIZE 16 LPDIRECTINPUT lpDirectInput; // DirectInput 對象實際上是一個com對象 LPDIRECTINPUTDEVICE lpKeyboard; // DirectInput 設(shè)備 BOOL InitDInput(HWND hWnd) { ??????HRESULT hr;// 創(chuàng)建一個 DIRECTINPUT 對象 ?????if( FAILED( hr = DirectInputCreate(hInstanceCopy, DIRECTINPUT_VERSION, &lpDirectInput, NULL))) ?????{// 失敗提示或處理 ???????returnhr; ??????}// 創(chuàng)建一個 DIRECTINPUTDEVICE 界面 //參數(shù) GUID_SysKeyboard 指明了建立的是鍵盤對象 ??????if( FAILED( hr = lpDirectInput->CreateDevice(GUID_SysKeyboard, &lpKeyboard, NULL))) ??????{ ????????????// 失敗提示或處理 ????????????returnhr; ???????}// 設(shè)定為通過一個 256 字節(jié)的數(shù)組返回查詢狀態(tài)值 ??????if( FAILED(hr = lpKeyboard->SetDataFormat(&c_dfDIKeyboard))) ??????{ ??????// 失敗提示或處理 ????????????return hr; ??????}// 設(shè)定協(xié)作模式為獨占模式和前臺模式,獨占模式表面本程序在運行中占有所有鍵盤資源,而前臺模式指出當(dāng)程序具有焦點時才可以占有鍵盤資源 ??????if( FAILED( hr = lpKeyboard->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND))) ??????{ ??????// 失敗提示或處理 ????????????returnhr; ??????} ??????// 設(shè)定緩沖區(qū)大小 ??????// 如果不設(shè)定,緩沖區(qū)大小默認(rèn)值為 0,程序就只能按立即模式工作 ??????// 如果要用緩沖模式工作,必須使緩沖區(qū)大小超過 0 ??????// 在此,我們沒有必要設(shè)定,因為我們就用立即模式工作(還有一種緩沖模式),所有我們將其注調(diào)了 ??????/* DIPROPDWORD property; ??????property.diph.dwSize = sizeof(DIPROPDWORD); ??????property.diph.dwHeaderSize = sizeof(DIPROPHEADER); ??????property.diph.dwObj = 0; ??????property.diph.dwHow = DIPH_DEVICE; ??????property.dwData = DINPUT_BUFFERSIZE; ??????if( FAILED(hr = lpKeyboard->SetProperty(DIPROP_BUFFERSIZE, &property.diph))) ??????{ ??????// 失敗 ????????????return FALSE; ??????} ??????*/ ??????//此處是關(guān)鍵,我們要通過這個函數(shù)來鎖定鍵盤,記住,所有的DirectInput資源在使用前都要鎖定,在此即獲得鍵盤資源,在知識我們剛才設(shè)定的鍵盤模式才能起作用 ??????hr = lpKeyboard->Acquire(); ??????if FAILED(hr) ??????{ ????????????// 失敗 ????????????return FALSE; ??????} ??????returnTRUE; ??????} |
在這段代碼中,我們首先定義了 lpDirectInput 和 lpKeyboard 兩個指針,前者指向 DIRECTINPUT 對象,后者指向一個 DIRECTINPUTDEVICE 界面。其順序就是這樣的。這和其它COM對象的使用方法都一樣,即先創(chuàng)建 COM 對象,然后創(chuàng)建界面,然后再獲得 硬件資源,然后使用資源,然后釋放。 通過 DirectInputCreate(), 我們?yōu)?lpDirectInput 創(chuàng)建了一個 DIRECTINPUT 對象。然后我們調(diào)用 CreateDevice 來建立一個DIRECTINPUTDEVICE 界面。 完成這些工作以后,我們便調(diào)用 DIRECTINPUTDEVICE 對象的 Acquire 方法來激活對設(shè)備的訪問權(quán)限。在此要特別說明一點,任何一個 DIRECTINPUT 設(shè)備,如果未經(jīng) Acquire,是無法進(jìn)行訪問的。還有,當(dāng)系統(tǒng)切換到別的進(jìn)程時,必須用 Unacquire 方法來釋放訪問權(quán)限,在系統(tǒng)切換回本進(jìn)程時再調(diào)用 Acquire 來重新獲得訪問權(quán)限。 立即模式的數(shù)據(jù)查詢 HRESULT ReadImmediateData( HWND hWnd ) { ??????HRESULT hr; ??????BYTE diks[256];// 創(chuàng)建鍵盤狀態(tài)數(shù)據(jù)緩沖區(qū)存取鍵盤信息 ??????inti; // 計數(shù)器 ??????if( NULL == g_pKeyboard ) ????????????returnS_OK;// 鍵盤狀態(tài)數(shù)據(jù)緩沖區(qū)清0 ??????ZeroMemory( &diks, sizeof(diks) );// 獲得鍵盤所有鍵的信息,這只是檢查一次 ??????hr = g_pKeyboard->GetDeviceState( sizeof(diks), &diks ); ??????if( FAILED(hr) ) ??????{ ????????????// 如果鍵盤資源丟失,我們要重新獲得 ????????????hr = g_pKeyboard->Acquire(); ????????????while( hr == DIERR_INPUTLOST ) ????????????hr = g_pKeyboard->Acquire(); ????????????returnS_OK; ??????}// 進(jìn)行一下輪循,處理鍵盤信息。 ??????for( i = 0; i < 256; i++ ) ??????{ ????????????if( diks[i] & 0x80 ) //記錄此鍵的狀態(tài),低字節(jié)最高位是 1 表示按下,0 表示松開,一般用 diks[i]&0x80 來測試 ????????????{ ??????????????????switch(i) ??????????????????{ ??????????????????????????????//我們可以通過測試計數(shù)器i,來判斷是哪個鍵被按下了。 ??????????????????????????????//我們提供幾個數(shù)據(jù) UP:200 down:208 left:203 right:205 enter:28 space:57 ??????????????????????????????//其實你可以用DirectX中的Samples\C++\DirectInput\Bin\Keyboard.exe程序來測試,只不過那是用 ??????????????????????????????//16進(jìn)制顯示的。 ????????????????????????case200: ??????????????????????????????break; ????????????????????????case0xc8: ??????????????????????????????break; ??????????????????} ????????????} ??????} ??????returnS_OK; } |
請注意,上面的這段代碼只是一個示例,重在使你明白其原理,但并不能滿足游戲的需求,因為這其中只查詢了一次鍵盤的全部信息,做了一次輪循,而在游戲中要周期性地查詢,并輪循,這就需要你自己用Win32 API函數(shù) SetTimer和 KillTimer 設(shè)置初始化 DirectInput 對象函數(shù)中在相應(yīng)的地方設(shè)置計計時器,讓windows定時向程序發(fā)送 WM_TIMER消息,你要通過此消息進(jìn)行周期性地鍵盤查詢,并在相應(yīng)的地方解除計時器。 最后一個函數(shù)是用于釋放指針或DirectInput對象的 void ReleaseDInput(void) { ??????if(lpDirectInput) ??????{ ????????????if(lpKeyboard) ????????????{ ??????????????????// Always unacquire the device before calling Release(). ??????????????????lpKeyboard->Unacquire(); ??????????????????lpKeyboard->Release(); ??????????????????lpKeyboard = NULL; ????????????} ????????????lpDirectInput->Release(); ????????????lpDirectInput = NULL; ??????} } |
在這些函數(shù)中的注釋很明確,關(guān)鍵在于理解其原理,而怎樣將他們?nèi)谌氲?Win32 API 程序的基本框架中的,在<<動畫程序編寫——DirectDraw之旅>> 1-3中的示例代碼中已經(jīng)解釋得很明確了,在此不再贅述。不過我們提供其中的代碼示例下載。同時你也可以去仔細(xì)閱讀DirectX 8.0 SDK 包中的 samples\ multimedia\ directdraw\ fullscreenmode 或\ samples\ multimedia\ directdraw\ windowedmode 這兩個工程中的文件,因為為了我們的示例也是照這兩個工程改編過來的,讀者可以通過仔細(xì)閱讀代碼和對比我們的更改,而更加了解 DirectDraw的運行運行原理。(請注意:是 DirectX 8.0 SDK 包中的示例,而在 9.0 中 DirectX SDK 已經(jīng)不提供 DirectDraw的示例代碼了) 我們就用這七個函數(shù)就已經(jīng)可以創(chuàng)造出一個小游戲了。 我們下面就要利用<<動畫程序編寫——DirectDraw之旅>> 1-3 中所用的代碼進(jìn)行進(jìn)一部的游戲開發(fā)。 我們先展示一下 DirectX中\ samples\ multimedia\ directdraw\ windowedmode 工程中的截圖 在這個動畫中有黑色背景,并有很多 DirectX 精靈在漂浮。 
這是一個全屏的動畫程序,而我們在<<動畫程序編寫——DirectDraw之旅>> 1-3其中做的改動就是為其加了一個背景,改屏幕分辨率 640×480 為 1024×768.注意,因為我們應(yīng)用的是全屏模式,即可以獨占顯存資源,所以我們可以更改屏幕的分辨率。這只是做的小小的改動,而我們的目的只在于讓大家更加深入了解。且看下面的這副截圖:

而我們還要繼續(xù)深入編程,我們的思路是,先將程序由先前的全屏程序改編成一個windows的窗口程序,然后將其所有的界面翻新,并改編 DirectX精靈為許多小蘑菇在漂浮,還要加入DirectInput 的組建,用鍵盤控制一個小娃娃。可以上下左右,并可以斜向飛行。 我們先將此動畫的截圖展現(xiàn)給大家 
怎么樣,你有什么想法,是想說:“唉,這還不好辦,就是又多加了一個!”,但不要光看截圖,不要忘記,我們一定讓她動起來,并且是可以控制的,這就不是那么簡單的事了! 什么?若有人看到這里感到有些迷茫和泄氣,不禁想問:“你說了這么多,那么源代碼在那里呢!,光給我們幾個函數(shù),又能做什么呢?”,如果你這么想,你也不要太急迫。我們還是先分析一下程序框架吧。 不過,還有一件重要的事情,我還是要重申一邊。一定要將 DirectX 的頭文件價,和lib文件夾加入到 Visual C++.NET 的默認(rèn)目錄中去,這樣編譯器就可以正確地找到它們了。 如果你不會加入,就請通過工具欄上的 Tool -> Option… 打開Option 對話框,設(shè)置如圖: 
好了,這樣我們的準(zhǔn)備工作就算已經(jīng)做好了。
來看看我們的工程文件結(jié)構(gòu)吧,還有工程中的資源。  
在工程資源中我們的 ID 號是都用的字符串表示的,筆者認(rèn)為這樣更加方便。 我想對于工程文件中的 ddutil.cpp 和 dxutil.cpp 文件,讀者如果了解有些 DirectDraw編程是不會感到陌生的,我們只是將其引入到我們的工程中了。而我們自己實際編程的文件是 outfly.cpp 文件。 我們的程序敘述如下: 首先進(jìn)行宏定義,結(jié)構(gòu)設(shè)置,和全局變量的聲明。 后在 WinMain (windows程序的入口點)中首先初始化一切需要初始化的物件(有windows窗口,DirectDraw對象,和 DirectInput對象),在此我們就調(diào)用前文講過的函數(shù),但要有寫改動,讀者在會在后面看到的。然后進(jìn)入消息循環(huán),在其中沒有消息時,程序會自動更新畫面,在有消息時處理消息。 當(dāng)遇到 WM_QUIT 消息后,結(jié)束整個程序。 我們在一些地方有一些小小的改動,我們來看看吧。 1 我們在 HRESULT InitDirectInput( HWND hWnd ) 函數(shù)中的開始加入了 KillTimer( hWnd, 0 ); FreeDirectInput(); |
關(guān)掉上一次使用的計時器,并釋放 DirectInput 設(shè)備。 而在最后加入了 SetTimer( hWnd, 0, 1000 / 100, NULL ); |
用來重新設(shè)置計時器。 2 我們在主窗口的消息處理函數(shù)中加入了 case WM_ACTIVATE: //當(dāng)程序先失去焦點,而現(xiàn)在有重新得到焦點時,要重新鎖定鍵盤資源 ??????if( WA_INACTIVE != wParam && g_pKeyboard ) ??????{ ????????????// Make sure the device is acquired, if we are gaining focus. ????????????g_pKeyboard->Acquire(); ??????} ??????break; caseWM_TIMER: //因為設(shè)置了計時器所以要處理此消息 ??????if( FAILED( ReadImmediateData( hWnd ) ) ) ??????{ ????????????KillTimer( hWnd, 0 ); ????????????MessageBox( NULL, _T("Error reading input state. ") ????????????_T("The sample will now exit."), ????????????_T("Keyboard"), MB_ICONERROR | MB_OK ); ??????} ??????break; caseWM_DESTROY:// Cleanup and close the app ??????FreeDirectDraw(); ??????FreeDirectInput();// 釋放資源 ??????PostQuitMessage( 0 ); ??????return 0L; |
3 在HRESULT ReadImmediateData( HWND hWnd ) 函數(shù)中進(jìn)行了這樣的處理,來時時改變小娃娃的坐標(biāo)。 for( i = 0; i < 256; i++ ) { ??????if( diks[i] & 0x80 ) ??????{ ????????????switch(i) ????????????{ ??????????????????case200: //上鍵 ????????????????????????if( g_me.fPosY > g_me.fVelY) ??????????????????????????????g_me.fPosY -= g_me.fVelY; ????????????????????????else ??????????????????????????????g_me.fPosY = 0; ????????????????????????break; ??????????????????case208: //下鍵 ????????????????????????if( g_me.fPosY <= WINDOW_HEIGHT - SPRITE_DIAMETER - g_me.fVelY) ??????????????????????????????g_me.fPosY += g_me.fVelY; ????????????????????????else ??????????????????????????????g_me.fPosY = WINDOW_HEIGHT- SPRITE_DIAMETER; ????????????????????????break; ??????????????????case203://左鍵 ????????????????????????if( g_me.fPosX > g_me.fVelX) ??????????????????????????????g_me.fPosX -= g_me.fVelX; ????????????????????????else ??????????????????????????????g_me.fPosX = 0; ????????????????????????break; ??????????????????case205://右鍵 ????????????????????????if( g_me.fPosX <= WINDOW_WIDTH - SPRITE_DIAMETER - g_me.fVelX) ??????????????????????????????g_me.fPosX += g_me.fVelX; ????????????????????????else ??????????????????????????????g_me.fPosX = WINDOW_WIDTH- SPRITE_DIAMETER; ????????????????????????break; ????????????} ??????} } |
這些只是其中一些比較重要的改動,還有許多改動,讀者會在實際的程序中看到的。如果你覺得:“啊!到這里就結(jié)束了,可是我還是感到似乎莫不到頭緒,就這樣草草收尾了?”,其實文章并沒有結(jié)束,重頭戲還在后面呢,那就不是我的工作了,而是看你有沒有耐心去仔細(xì)閱讀代碼了,因為想要把握程序的整體,與其讓我將代碼放在文章中,還不如讀者自己在編譯器中自己運行實踐一下好,其實我們已經(jīng)在第二個工程代碼中有過詳細(xì)的解釋。但記住一定要按照順序閱讀 工程1,工程2 ,工程3。工程1就是 DirectX中提供的原代碼,工程2就是我們改了一個背景的工程,而3就是我們討論的工程。 |