詳細講解DirectDraw程式設計基礎(轉)

post0發表於2007-08-12
詳細講解DirectDraw程式設計基礎(轉)[@more@]

  本文面向有幾個月學習程式設計經歷的初學者:看過C++的教程,看的懂基本的C++語法;有點點VC使用經驗,知道怎麼去組建一個工程;理解一些windows程式設計的基本概念,比如視窗、訊息迴圈等;還有,不懂的地方會去查資料:)。

  看過幾本關於DirectDraw的書,這些書都不錯,在此感謝她們的作者。美中不足的是這些書的部分起點較高,雖然我們仍然能夠清晰的理解一些概念,但在組織這些檔案上會有不少困惑。在此我重申一下書中的概念,也藉此梳理一下自己的思路。廢話少說,言歸正傳。

  首先說一些不可不說的東西。我認為它們不可不提,是因為這些東西也許太基礎,高手們往往忽略這些東西對新手的作用。作為一個新手,我覺得掌握程式的框架及組織方法,比多熟悉幾個APIs更迫切一些。Now lets begin:

  寫一個遊戲程式,要熟悉其流程,另外要鍛鍊組織程式檔案的能力。對新手來說,我建議按部就班的來處理及分析要寫的程式,不主張這個時候你在搞思維跳躍。這是個良好的習慣,當然也有利於我們儘快掌握程式設計的思想方法。下面來看一個概括的流程及相應的程式框架

  

  (框架顯示不出來。。)

  

  那麼,如何利用上面的流程來構建我們的大體程式框架呢?

  

  我們已經知道一些windows程式設計方面的東西了,也許你還比較瞭解MFC。我們這裡不提倡用MFC,儘管它封裝了好多有用的模式,但對我們編遊戲來說,倒是累贅了。好,接著說。既然採用windowsAPI,可以建立個檔案WinMain.cpp來處理windows程式設計中有關視窗的一些問題。這樣,我們在該檔案中應該完成建立視窗,處理基本訊息(比如按“esc”退出等),控制程式退出等。遊戲過程中視窗的訊息是不是也要在這處理呢?當然,不過遊戲當中的視窗就不僅是windows視窗了,顯示部分要靠DirectDraw來控制,那麼我們只好在WinMain.cpp中呼叫相關的模組來處理。這麼看來,在WinMain.cpp中幾乎囊括了整個流程,不錯,它就控制了程式的整個框架,為你的程式核心提供了一個平臺。平臺有了,那麼下一步,GameMain.cpp要誕生了,這個主要用來控制整個遊戲的各個元件,協調各部分工作,完成遊戲設定初始化,遊戲中訊息迴圈,控制遊戲退出。你的才華就在這兒來盡情的發揮了。一般,遊戲程式會有幾個固定的元件的:顯示,音樂,資訊輸入。在DirectX中提供了很方便的元件DirectDraw,DirectSound和DirectMusic,DirectInput。相應的我們建立MyDirectDraw.cpp,MyDirectAudio.cpp,MyDirectInput.cpp來控制各部分元件的相應功能。

  顯然,這3部分都是為GameMain.cpp服務的,被GameMain.cpp呼叫。那麼我們可以看出我們的程式應該包括的檔案及其包含關係為:

  

  (圖表顯示不出來了,555)

  

  程式檔案怎麼去組織,應該由這個表可以看出來。這麼一看,我們發現,WinMain.cpp好像是一個投資者,提供開發平臺,他只關注整個專案總的程式,不關注細節。GameMain.cpp好像個專案負責人,整個專案的細節過程由他來策劃,來控制,向上與WinMain.cpp互動,來完成專案,向下協調MyDirectDraw.cpp,MyDirectAudio.cpp,MyDirectInput.cpp之間的工作。MyDirectDraw.cpp,MyDirectAudio.cpp,MyDirectInput.cpp這三個傢伙就是員工了,負責各自的工作,完成相應的功能給GameMain.cpp。

  

  組織程式應該就是這麼個思路,當然具體問題具體分析。那麼我們下面來開始看DirectDraw部分了。

  

  首先,做準備工作,安裝DirectX SDK,在VC中新增dxguid.lib和ddraw.lib(本來不想說這個,看到有個教程,它少加了dxguid.lib,鬱悶了我好一陣子,害人頗深感覺)這樣,directdraw程式才能透過編譯。提一下,dxguid.lib中定義了DirectX中會用到的所有全域性控制程式碼,ddraw.lib是DirectDraw使用的函式庫。

  

  下面就可以寫程式碼了,這裡我們當然主要看MyDirectDraw.cpp該怎麼寫了

  為此,我選出了幾個原始碼,做參考研究,它們會與本文一起打包。

  我還是習慣先從整體上鳥瞰一下:

  

  一般,在MyDirectDraw.cpp(注意不要忘記引用標頭檔案ddraw.h)中至少要有兩部分:初始化和結束。先看初始化,所謂初始化無非是個準備工作,需要的東西定義建立出來擺在手邊以備後用。來看看初始化函式intMyDirectDrawInit(void)該怎麼寫。首先定義一個指向DirectDraw物件的指標,建立DirectDraw物件,查詢以獲取最新的DirectDraw介面,設定協作等級,設定顯示模式。透過這些步驟可以建立一個黑色的螢幕了,也就是說已經開闢了我們需要的空間了,當然DirectDraw程式的初始化不會這麼簡單。要操作2d圖形,我們還要接著建立主頁面和緩衝頁面以及離屏頁面,總之根據需要,凡是需要在操作前需要準備好的東西都可以放在這裡。那麼結束 int MyDirectDrawShut(void)就應該釋放我們開闢的東西,一般要釋放主頁面指標,和DirectDraw介面等。

  

  大體就是這麼個樣子,go on,該細一點了,呵呵

  

  先定義指標:LPDIRECTDRAW lpDDraw_temp;代表整個顯示系統

  建立物件: if (FAILED(DirectDrawCreate(NULL, &lpDDraw_temp, NULL)))

  {

  MessageBox(NULL,TEXT("Direct Draw Create error!"),

  TEXT("Wrong!"),MB_OK);

  return(0);

  }

  

  這裡用了一個FAILED宏來檢測是否建立成功,這可以幫我們跟蹤錯誤。

  

  函式DirectDrawCreate(NULL, &lpDDraw_temp, NULL)完成建立,第一個引數是顯示驅動的全域性唯一標誌符,這裡null表示目前的顯示裝置;第二個引數用來接受建立出來的DirectDraw物件地址,這裡用&lpDDraw_temp接受;第三個引數?不要問,就給它null,不想惹麻煩的話。

  

  查詢DirectDraw介面:if(FAILED(lpDDraw_temp->QueryInterface(IID_IDirectDraw7, (LPVOID *)&lpDDraw7)))

  {

  MessageBox(NULL,TEXT("DirectDraw QueryInterface error!"),

  TEXT("Wrong!"),MB_OK);

  return(0);

  }

  

  透過QueryInterface()方法來獲取新介面,這裡是IDirectDraw7而不是IDirectDraw8,指向IDirectDraw7的指標放在lpDDraw7中,這是個全域性變數,可以這樣定義LPDIRECTDRAW7 lpDDraw7=NULL;

  

  順便說一下,一般情況下你是應該知道你使用的介面的,這和SDK有關,所以說這一步不是必須的。

  

  設定協作等級: if (FAILED(lpDDraw7->SetCooperativeLevel(main_window_handle, DDSCL_FULLSCREEN | DDSCL_ALLOWMODEX | DDSCL_EXCLUSIVE | DDSCL_ALLOWREBOOT)))

  {

  MessageBox(NULL,TEXT("DirectDraw SetCooperativeLevel error!"),

  TEXT("Wrong!"),MB_OK);

  return(0);

  }

  

  決定你這個程式和windows的關係,它向windows申請所用資源,比如它要全屏,獨佔等。第一個引數是主視窗控制程式碼,就是你WinMain()中建立的那個了,第二個引數有幾個控制標誌,常用的用法如下:

  

  DDSCL_FULLSCREEN:全屏模式,必須和DDSCL_EXCLUSIVE同時使用

  DDSCL_EXCLUSIVE:請求獨佔級別,須和DDSCL_FULLSCREEN同時使用

  DDSCL_ALLOWREBOOT:允許系統檢測ctrl+alt+del按鍵訊息(這很有用)

  

  我想,這三個就夠用了,其他的就先不用管了

  設定顯示模式:if(FAILED(lpDDraw7->SetDisplayMode(800, 600, 16,0,0)))

  {

  MessageBox(NULL,TEXT("DirectDraw SetDisplayMode error!"),

  TEXT("Wrong!"),MB_OK);

  return(0);

  }

  

  遊戲中要使用的顯示模式可能和使用者當前顯示模式不一樣,要在此統一設定SetDisplayMode()強制使用它設定的模式,它的前三個引數很容易懂吧,第四個,用0表示使用預設的重新整理率,第五個引數這裡是0,有書上說必須用DDSDM_STANDVGAMODE(可以理解,只是不知道這個0什麼意思,我想應該是default的意思吧。

  

  到此為止,我想已經建立出來我們需要的空間了,以後,隨著我們要求的提高,再逐步完善初始化函式,now看看結束函式:

  釋放介面: if (lpDDraw7)

  {

  lpDDraw7->Release();

  lpDDraw7 = NULL;

  }

  

  以後還要釋放主頁面,緩衝頁面等,需要注意一點的是一定要釋放你申請的資源,這是個好習慣,更應該注意的一點是先建立的一定要後釋放,因為後建立的可能是在先建立的環境下工作的。

  

  到此為止,我們只是做好了最基礎的準備工作,什麼還都不能做呢

  想做點什麼嗎?歇會吧,說點不得不說的題外話:

  

  那麼我們來看看顏色吧。有關色彩,分這麼幾種,256色(8位的),16位增強色,24位真彩和32位真彩。256色估計很少用了,16位目前還是主流,所以我們著重看一下16位增強色,通常16位增強色有兩種格式:5.5.5和5.6.5,一般用RGB表示法表示。其中:

  5.5.5格式,最高位為Alpha位,表示是不是透明,其餘15位表示顏色,紅綠藍各5位,這種格式可以表示32786種顏色。透過宏

  

  #define _RGB16BIT555(r,g,b)((b%32)+((g%32)<<5)+((r%32)<<10))來轉變成5.5.5格式

  

  對5.6.5格式,顯然,紅藍各5位,綠6位,這樣可以表示65536種顏色,同樣,宏

  

  #define _RGB16BIT565(r,g,b) ((b%32)+((g%64)<<6)+((r%32)<<11))來轉變成5.6.5格式

  

  中間的移位我也搞不清楚是怎麼回事,姑且先不看了,看的越多可能越胡塗哦

  那麼到底該用哪種格式?看機器了,大部分可以用5.6.5,當然你可以檢測一下,至於怎麼檢測嘛,我就不說了,查查相關資料就可以了。24位呢?紅綠藍各8位唄,32位?添個Alpha位,其餘同24位。好了顏色就說到這裡。

  

  下面想幹嘛?想在螢幕上搞點顏色出來,參看附的原始碼code1

  你會不會發現我們還應該在上面的基礎上添點什麼?

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-951818/,如需轉載,請註明出處,否則將追究法律責任。

相關文章