1. 程式人生 > >【Visual C++】遊戲開發五十六 淺墨DirectX教程二十三 打造遊戲GUI介面(一)

【Visual C++】遊戲開發五十六 淺墨DirectX教程二十三 打造遊戲GUI介面(一)

眾所周知,GUI是遊戲中不可缺少的元素,這篇文章中,我們首先了解了遊戲GUI介面的知識與相關概念,然後一起設計了一個封裝好GUI圖形介面的C++類。這個類有著非常強的擴充套件性,使用也是極其方便,很適合二次開發。

先看一張實現的效果圖吧:


其中的背景音樂,遊戲圖示和背景圖片都出自育碧公司的招牌式大作《刺客信條》。

程式的視窗大小已經被淺墨調成了1366 x768,現階段比較流行的筆記本解析度尺寸(其實真的想吐槽這個奇葩萬年不變的電腦螢幕解析度,在如今的後PC時代。手機螢幕都開始對1920 x1080的解析度不滿足了。。。。)

嗯,開始正文吧。

一、UI和GUI的概述

首先我們看一下UI的比較正統的定義:使用者介面(User Interface,簡稱UI,亦稱使用者介面)是系統和使用者之間進行互動和資訊交換的媒介,它實現資訊的內部形式與人類可以接受形式之間的轉換。

使用者介面是介於使用者與硬體之間,為彼此之間互動溝通而設計的相關軟體,使得使用者能夠方便有效地去操作硬體以達成雙向之互動,完成所希望的工作,使用者介面定義廣泛,包含了人機互動與圖形使用者介面,凡參與人類與機械的資訊交流的領域都存在著使用者介面。

而GUI的正統定義是:圖形使用者介面(Graphical User Interface,簡稱 GUI,又稱圖形使用者介面)是指採用圖形方式顯示的計算機操作使用者介面。與早期計算機使用的命令列介面相比,圖形介面對於使用者來說在視覺上更易於接受。

我們知道,在遊戲中,能夠和遊戲玩家進行互動是至關重要的。這樣的交流可以是文字形式的,視覺顯示,聲音訊號等等。而對於視覺顯示而言,絕大多數遊戲毋庸置疑就是用圖形使用者介面(GUI)來和遊戲玩家進行互動的。而在遊戲過程中,依舊少不了GUI介面的出場,比如說狀態顯示介面(Head-up Display,HUD),HUD,這個介面提供一切和遊戲有關的資訊。用於提供玩家剩餘任務時間,生命值,座標位置,以及更多資訊。

讓我們欣賞一部分近期的3A遊戲大作的UI介面美圖吧:

《鬼泣5》:

 

《英雄無敵6》:

 

 欣賞完了,我們繼續開講吧。

二、關於狀態顯示介面(HUD)

這一節裡讓我們一起科普HUD的概念。

我們知道,通常情況下在遊戲中螢幕上會有很多的小的介面模組配上文字,為玩家提供了一些有用的資訊,比如玩家剩餘任務時間,生命值,魔法值,座標位置等等更多的資訊。而因為在玩家和遊戲互動的過程中,這些介面是透過攝像機視角顯示出來的,它們被稱為HUD(heads-up-display)。在視訊遊戲中,HUD就是一類可以實時向玩家顯示有用資訊和符號的GUI,並且它和諸如遊戲選單這樣的其他介面不同.HUD中通常沒有按鈕、編輯框或者適合玩家互動的內容,因為玩家往往都是被遊戲本身的畫面和內容所吸引,而不是去關注HUD。所以呢,HUD通常並不去使用可以互動的元素,而傾向於使用文字和影象來表示某些資訊。所以,遊戲選項和偏好設定這樣的內容不是HUD的菜,它們直接交由系統選單來完成。

下面我們看一看更加規範化的HUD的介紹。

HUDheads-up-display,硬著過來翻譯就是“擡頭顯視裝置”- -。這是一個從軍事領域起源的技術,可以把一些重要的戰術資訊顯示在正常觀察方向的視野範圍內,而同時又不會影響對於環境的注意,也不用總是轉移視線去專門觀察儀表板上的那些指標和資料。遊戲借鑑了這個概念,把遊戲相關的資訊以類似HUD的方式顯示在遊戲畫面上,讓玩家可以隨時瞭解那些最重要最直接相關的內容。當然玩家要獲得遊戲資訊可以有別的方式,比如選單。選單有著專門的介面,可以容納更大的資訊量,但卻不能和遊戲畫面同時出現。調出選單意味著中斷遊戲流程,HUD則在提供必要的資訊的同時完全避免了這個問題。

雖然選單提供了大量的資訊,然而對於處在自由行動狀態下的玩家而言,這些資訊都不是立刻需要獲取的。HUD只提供了最重要最基本的內容:當前場景的地圖。切換到戰鬥狀態下之後,HUD所提供的資訊便會轉變為那些只和戰鬥相關的部分。

記得最早的遊戲pong在設計的時候就沒有HUD的。對於這樣一個簡單的遊戲而言,唯一對玩家有意義需要及時掌握的就是雙方的比分,而最早版本的pong沒有這個功能,玩家需要自己去記錄比分。遊戲設計者很快意識到了這個問題,HUD很快就被整合到了後來的遊戲之中,並隨著遊戲的進化一起演變,完善。

如果被上面的這些書面語繞暈了沒關係,說了這麼多,一言以蔽之:選單的目的是大而全,HUD的目的則是少而精。HUD主要注重的是實時向玩家顯示有用資訊和符號的GUI,和而系統選單GUI可以給玩家提供更多更全的資訊。

不同型別的遊戲玩起來的重點不一樣,HUD在提供的資訊方面也有很大的差別。我們不妨按照遊戲的類別,來看看各種遊戲的HUD設計的模式和重點。

1.角色扮演類遊戲

取決於遊戲是否會在行動場景和戰鬥場景之間切換,角色扮演遊戲的HUD設計會有所不同,關於行動的那一部分內容,比如地圖和方向指示之類的資訊可能會被單獨分列出來。但總體上角色扮演遊戲的HUD資訊量基本是一致的:玩家的生命,魔法,行動力,狀態(或者其它因遊戲而異的內容)等等的數值,玩家可以一鍵接觸到的物品魔法等等東西,如果物品和魔法的內容很複雜,那麼一般都會把全面調節的功能交給選單來完成。比如預設快捷鍵和選單游標位置記憶功能就是這樣的目的,把選單中全面而細微的調節能力中,挑選出一些最重要的,放到HUD上來,共玩家選擇使用。

2.格鬥遊戲

無論格鬥遊戲系統本身怎麼進化,從2D變到3D,格鬥遊戲的HUD總是保持著自己一貫的特色。格鬥遊戲的HUD大多分成兩個部分:第一,對戰鬥資料的統計,第二,對戰鬥中精彩場面的描述。前者就是那些顯眼的血槽,還包括時間,局數計分。後者則是對於連擊之類的精彩動作的積分等等資訊。其中血槽這個東西是遊戲HUD設計上一個非常典型的東西。

3.體育遊戲

體育類遊戲在設計HUD的問題上有著天然的兩個參考座標系:電視轉播畫面和球隊的戰術分析圖。遊戲的HUD結合了這兩者,即體現出了電視轉播畫面的現場感,也做出了戰術分析圖那樣的清晰感,讓玩家儘可能的在有身臨其境的感覺的同時也對比賽局面有著清晰的瞭解。實際上體育類遊戲的HUD設計作的是如此的好,以至於最近以來,很多體育專案的電視轉播畫面開始學習這類遊戲的HUD設計,往畫面上新增一些即時的比賽資訊和戰術分析。

4.駕駛模擬類遊戲

這類遊戲所要模仿的物件就是HUD這個概念的來源的地方,所以駕駛模擬類遊戲HUD設計的原則也就變得非常簡單而直接:儘可能的去重現模擬物件的原始HUD就可以了。當然根據遊戲模擬真實的程度不同,再現真實HUD設計的程度也有所區別。HUD的設計始終存在一個如何抽取最重要的資訊提示玩家的問題,過於模擬的遊戲HUD設計將大量的資訊不加選擇的堆在玩家面前。對於遊戲本身模擬這個概念而言,是好事,但對於像通過這些遊戲來體驗現實生活中不可能接觸到的東西這個目的而言,高度的模擬模擬往往會成為上手的障礙。遊戲畢竟是遊戲,遊戲的HUD如何在模擬模擬和抽象表現上把握平衡,是這類遊戲的一個突出問題。

5.動作射擊類遊戲

射擊類遊戲的HUD通常都包括了玩家的狀態,玩家的武器狀態,地圖,以及目標指示這四個方面。第三人稱的射擊遊戲或者團隊策略類射擊遊戲在HUD設計上和傳統的第一人稱射擊遊戲區別也不是很大,重點同樣在這些要素上。HUD的引入給所有的射擊類遊戲,無論遊戲本身的背景設定是在什麼樣的時代,都帶來了一定的科幻未來的要素。真實的戰鬥中,對於戰場情況的把握從來都是很大的挑戰,不斷進步的單兵資訊化裝備正是力求解決這些問題。射擊類遊戲通過HUD的引入超前的解決了這個問題。就目前而言,感覺做的最好的射擊類遊戲HUD恰恰就是一些未來題材的射擊遊戲,等下在HUD進化中我們會看到例子。

6.策略類遊戲

最為複雜的一類遊戲HUD,事實上在這類遊戲裡面HUD和選單往往難以截然區分。因為玩家幾乎每時每刻都需要確實的掌握整個遊戲空間裡大量單位的行動。HUD是簡化了的選單,但在很多的策略類遊戲上,簡化只是一個美好的願望,遊戲本身的複雜程度和玩家的上帝視角導致了這類遊戲必然隨時都有大量的資訊反饋和大量的命令等待輸入。策略類遊戲的HUD如此的複雜,以至於在控制器鍵位相對較少的遊戲主機上,這類遊戲的流行程度始終達不到PC上同類遊戲的流行度。遊戲本身機制導致的複雜HUD設計應該是這類遊戲受眾群體侷限性產生的原因之一。

其它型別的遊戲,比如平臺遊戲,益智遊戲等等在HUD設計上感覺並沒有什麼特別的地方。它們的HUD只要能清晰的交代出遊戲人物(如果有的話)的狀態和遊戲的進展程度(得分之類的資訊)就可以了。

如果看到這裡覺得累了,依舊是欣賞一部分近期的3A遊戲大作的UI介面美圖吧:

《孤島危機3》:


《仙劍奇俠傳五 前傳》:

 

《刺客信條4黑旗》:


三、開始GUI系統的設計

在這篇文章裡面,我們將一起去實現一個簡單而健全的GUI系統。這個系統既有HUD的功能,也可以建立GUI選單。依舊是和之前的其他系統一樣,封裝在一個類中,這次的類是D3DGUIClass。

下面又到了天馬行空的設計時刻了——在實現GUI系統之前,讓我們設計出心目中的GUI系統該有的功能。

1.大體說明

首先需要說明的是,我們這次設計的GUI系統主要用於演示之用,擴充套件性很強,需要的更多功能完全可以在這個GUI的系統上進行二次開發,來增強它的特性和功能。

GUI系統主要由按鈕和靜態文字控制元件物件組成,當然,還少不了背景圖的顯示。由於GUI系統是2D的,這意味著指定物件位置時,是用不到Z軸的。另外,我們決定使用正交投影的方式來渲染GUI,這樣可以根據畫素的位置來指定螢幕的位置。

讓我們來回憶一下,(0,0)對應的是螢幕的左上角。在windows系統中,左上角就是(0,0)。有了這個資訊,就可以輕鬆地確定玩家的滑鼠指標是否懸停在GUI系統的控制元件之上,或者說正在點選這個控制元件。

2.關於佈局規劃和背景圖

在設計和規劃按鈕和文字位置時,我們只要牢記視窗左上角的座標是(0,0),就能隨心所欲的建立各式各樣的限制的系統範圍內的心儀的GUI佈局出來。對於背景圖的話,其實沒必要考慮它的位置,因為這個影象就是一個由兩個三角形構成的全屏影象。只要系統知道當前程式的寬度和高度,就可以顯示一個全屏紋理圖。

3.關於按鈕控制元件的實現

必不可少的特性就是可以點選的按鈕控制元件,其實按鈕控制元件的實現比點選影象更容易實現。由於按鈕控制元件的位置是以畫素來指定的,因此為了確定滑鼠指標是否在按鈕上或者按下了按鈕,其實就是檢查當前滑鼠指標的位置是否落在按鈕區域內。因為Windows作業系統用畫素指定滑鼠位置且螢幕的左上角為(0,0),所以檢查滑鼠的座標是否處於按鈕四個角的座標範圍內就可以了,即檢查如下四個方面:

按鈕的左側座標位置是否小於滑鼠指標的X座標,按鈕右側座標位置是否大於滑鼠指標的X座標,按鈕的上側座標是否小於滑鼠指標的Y座標,下側座標是否大於滑鼠的Y座標。如果四個都為真的話,那麼就可以認定滑鼠指標就在按鈕控制元件之上了,然後便通過訊息過程來檢測是否按下的滑鼠左鍵這個狀態,如果按下了滑鼠左鍵,那麼就可以確定玩家不僅僅是把滑鼠放在了按鈕之上,而是同時點選了按鈕。我們還在這個GUI系統中實現了通過滑鼠的懸停和點選動作,來改變按鈕的外觀,在按下按鈕後有一定的動畫效果。

根據上面的描述,我們可以寫出的實現程式碼如下:

其中pControl是一個自定義的GUICONTROL結構體的指標物件

[cpp] view plaincopyprint?
  1. //檢查滑鼠是否懸停或者點選了按鈕
  2. if(mouseX> pControl->m_xPos && mouseX < pControl->m_xPos +pControl->m_width &&  
  3.          mouseY> pControl->m_yPos && mouseY < pControl->m_yPos + pControl->m_height)  
  4. {  
  5.          if(LMBDown)status = UGP_BUTTON_DOWN;  
  6.          elsestatus = UGP_BUTTON_OVER;  
  7. }  
                            //檢查滑鼠是否懸停或者點選了按鈕
                            if(mouseX> pControl->m_xPos && mouseX < pControl->m_xPos +pControl->m_width &&
                                     mouseY> pControl->m_yPos && mouseY < pControl->m_yPos + pControl->m_height)
                            {
                                     if(LMBDown)status = UGP_BUTTON_DOWN;
                                     elsestatus = UGP_BUTTON_OVER;
                            }

4.類的框架設計

因為這個GUI系統的實現還有一定的細節需要詳細說明,所以決定分兩次更新來講解。這次我們先把功能完整的類的實現結果給大家,並告訴大家如何使用,說明一下main函式相對於之前有哪些細節需要改變,然後GUI系統類的實現細節留待下篇講解(如果需要額外用一次更新來講解的話)。

還是把詳細註釋的標頭檔案貼一下吧:

[cpp] view plaincopyprint?
  1. //====================================================
  2. // Name: D3DGUIClass.h
  3. //      Des:一個遊戲GUI介面系統類的標頭檔案
  4. // 2013年 11月17日  Create by 淺墨
  5. //====================================================
  6. #pragma once
  7. // 所支援的控制元件型別巨集
  8. #define UGP_GUI_STATICTEXT                 1
  9. #define UGP_GUI_BUTTON                     2
  10. #define UGP_GUI_Background                 3
  11. // 滑鼠按鍵狀態巨集
  12. #define UGP_BUTTON_UP                      1
  13. #define UGP_BUTTON_OVER                2
  14. #define UGP_BUTTON_DOWN                    3
  15. // 設定一些GUI中用到的控制元件ID
  16. #define STATIC_ID_1  1
  17. #define STATIC_ID_2  2
  18. #define BUTTON_ID_1  3
  19. #define BUTTON_ID_2  4
  20. #define BUTTON_ID_3  5
  21. #define BUTTON_ID_4  6
  22. // FVF靈活頂點型別的結構體
  23. struct GUIVERTEX  
  24. {  
  25.          floatx, y, z, rhw;  
  26.          unsignedlong color;  
  27.          floattu, tv;  
  28. };  
  29. #define D3DFVF_GUI (D3DFVF_XYZRHW |D3DFVF_DIFFUSE | D3DFVF_TEX1)
  30. //控制元件屬性結構體
  31. struct GUICONTROL  
  32. {  
  33.          //操作型別,ID和顏色
  34.          intm_type;  //控制元件型別
  35.          intm_id;  //控制元件ID
  36.          unsignedlong m_color; //控制元件顏色
  37.          intm_listID;   //如果是文字的話,這個變數就表示它使用的字型,否則就表示頂點快取
  38.          floatm_xPos, m_yPos;   //控制元件的起始位置
  39.          floatm_width, m_height;   // 控制元件的寬度和高度
  40.          wchar_t*m_text;   // 文字內容
  41.          LPDIRECT3DTEXTURE9m_Background;   // 控制元件背景的填充影象
  42.          LPDIRECT3DTEXTURE9m_upTex, m_downTex, m_overTex;   // 存放按鈕彈起,按下和滑鼠經過時的3張紋理圖
  43. };  
  44. class D3DGUIClass  
  45. {  
  46. private:  
  47.          LPDIRECT3DDEVICE9m_pd3dDevice;  //D3D裝置物件    
  48.          LPD3DXFONT*m_pFonts;  //D3D字型物件
  49.          GUICONTROL*m_pControls;  //控制元件物件
  50.          LPDIRECT3DVERTEXBUFFER9*m_pVertexBuffer;   //頂點快取物件指標
  51.          GUICONTROLm_Background;  //背景圖物件
  52.          LPDIRECT3DVERTEXBUFFER9m_BackgroundBuffer;   //背景圖緩衝區物件
  53.          boolm_bIsBackgroundUsed;  //一個標識,用於標識是否已經用了背景
  54.          intm_nTotalFontNum;              //字型數目計數器
  55.          intm_nTotalControlNum;        //控制元件數目計數器
  56.          intm_nTotalBufferNum; //緩衝區數目計數器
  57.          intm_nWindowWidth;    //視窗寬度
  58.          intm_nWindowHeight;   //視窗高度
  59. public:  
  60.          D3DGUIClass(LPDIRECT3DDEVICE9device, int w, int h);  
  61.          ~D3DGUIClass(){ ClearUp(); }  
  62.          LPDIRECT3DDEVICE9GetD3dDevice() { return m_pd3dDevice; } //返回D3D裝置物件的函式
  63.          GUICONTROL*GetBackground() { return &m_Background; } //返回背景的函式
  64.          LPDIRECT3DVERTEXBUFFER9GetBackgroundBuffer() { return m_BackgroundBuffer; }  //返回背景緩衝區物件的函式
  65.          intGetTotalFontNum() { return m_nTotalFontNum; } //返回所有字型數目的函式
  66.          intGetTotalControlNum() { return m_nTotalControlNum; }  //返回所有控制元件數目的函式
  67.          intGetTotalBufferNum() { return m_nTotalBufferNum; } //返回總的緩衝區數目的函式
  68.          intGetWindowWidth() { return m_nWindowWidth; } //返回視窗寬度的函式
  69.          intGetWindowHeight() { return m_nWindowHeight; } //返回視窗高度的函式
  70.          boolIsBackgroundUsed() { return m_bIsBackgroundUsed; }     //返回背景是否在使用的bool值的函式
  71.          voidSetWindowSize(int w, int h) { m_nWindowWidth = w; m_nWindowHeight = h; }  //設定視窗寬度和高度的函式
  72.          LPD3DXFONTGetFont(int id)  //返回字型ID函式
  73.          {  
  74.                    if(id< 0 || id >= m_nTotalFontNum) return NULL;  
  75.                    returnm_pFonts[id];  
  76.          }  
  77.          GUICONTROL*GetGUIControl(int id)  //返回GUI控制元件ID函式
  78.          {  
  79.                    if(id< 0 || id >= m_nTotalControlNum) return NULL;  
  80.                    return&m_pControls[id];  
  81.          }  
  82.          LPDIRECT3DVERTEXBUFFER9GetVertexBuffer(int id) //返回頂點快取ID函式
  83.          {  
  84.                    if(id< 0 || id >= m_nTotalBufferNum) return NULL;  
  85.                    returnm_pVertexBuffer[id];  
  86.          }  
  87.          boolCreateTextFont(wchar_t *fontName, int size, int *fontID);  //字型建立函式
  88.          boolAddBackground(wchar_t *fileName);  //GUI背景新增函式
  89.          boolAddStaticText(int id, wchar_t *text, float x, float y, unsigned long color, intfontID); //新增靜態文字函式
  90.          boolAddButton(int id, float x, float y, wchar_t *up, wchar_t *over, wchar_t *down);//新增按鈕函式
  91.          voidClearUp( ); //資源清理函式
  92. };  
  93. void ProcessGUI(D3DGUIClass *gui, boolLMBDown, int mouseX, int mouseY,  
  94.          void(*funcPtr)(intid, int state));  //回撥函式
//====================================================
// Name: D3DGUIClass.h
//      Des:一個遊戲GUI介面系統類的標頭檔案
// 2013年 11月17日  Create by 淺墨
//====================================================
 
#pragma once
 
 
// 所支援的控制元件型別巨集
#define UGP_GUI_STATICTEXT                 1
#define UGP_GUI_BUTTON                     2
#define UGP_GUI_Background                 3
 
// 滑鼠按鍵狀態巨集
#define UGP_BUTTON_UP                      1
#define UGP_BUTTON_OVER          	   2
#define UGP_BUTTON_DOWN                	   3
 
// 設定一些GUI中用到的控制元件ID
#define STATIC_ID_1  1
#define STATIC_ID_2  2
#define BUTTON_ID_1  3
#define BUTTON_ID_2  4
#define BUTTON_ID_3  5
#define BUTTON_ID_4  6
 
 
 
// FVF靈活頂點型別的結構體
struct GUIVERTEX
{
         floatx, y, z, rhw;
         unsignedlong color;
         floattu, tv;
};
#define D3DFVF_GUI (D3DFVF_XYZRHW |D3DFVF_DIFFUSE | D3DFVF_TEX1)
 
 
//控制元件屬性結構體
struct GUICONTROL
{
         //操作型別,ID和顏色
         intm_type;  //控制元件型別
         intm_id;  //控制元件ID
         unsignedlong m_color; //控制元件顏色
         intm_listID;   //如果是文字的話,這個變數就表示它使用的字型,否則就表示頂點快取
         floatm_xPos, m_yPos;   //控制元件的起始位置
         floatm_width, m_height;   // 控制元件的寬度和高度
         wchar_t*m_text;   // 文字內容
         LPDIRECT3DTEXTURE9m_Background;   // 控制元件背景的填充影象
         LPDIRECT3DTEXTURE9m_upTex, m_downTex, m_overTex;   // 存放按鈕彈起,按下和滑鼠經過時的3張紋理圖
};
 
 
 
class D3DGUIClass
{
 
private:
         LPDIRECT3DDEVICE9m_pd3dDevice;  //D3D裝置物件    
         LPD3DXFONT*m_pFonts;  //D3D字型物件
         GUICONTROL*m_pControls;  //控制元件物件
         LPDIRECT3DVERTEXBUFFER9*m_pVertexBuffer;   //頂點快取物件指標
         GUICONTROLm_Background;  //背景圖物件
         LPDIRECT3DVERTEXBUFFER9m_BackgroundBuffer;   //背景圖緩衝區物件
 
         boolm_bIsBackgroundUsed;  //一個標識,用於標識是否已經用了背景
         intm_nTotalFontNum;              //字型數目計數器
         intm_nTotalControlNum;        //控制元件數目計數器
         intm_nTotalBufferNum; //緩衝區數目計數器
 
         intm_nWindowWidth;    //視窗寬度
         intm_nWindowHeight;   //視窗高度
 
 
 
public:
         D3DGUIClass(LPDIRECT3DDEVICE9device, int w, int h);
         ~D3DGUIClass(){ ClearUp(); }
 
         LPDIRECT3DDEVICE9GetD3dDevice() { return m_pd3dDevice; } //返回D3D裝置物件的函式
         GUICONTROL*GetBackground() { return &m_Background; } //返回背景的函式
         LPDIRECT3DVERTEXBUFFER9GetBackgroundBuffer() { return m_BackgroundBuffer; }  //返回背景緩衝區物件的函式
 
 
         intGetTotalFontNum() { return m_nTotalFontNum; } //返回所有字型數目的函式
         intGetTotalControlNum() { return m_nTotalControlNum; }  //返回所有控制元件數目的函式
         intGetTotalBufferNum() { return m_nTotalBufferNum; } //返回總的緩衝區數目的函式
         intGetWindowWidth() { return m_nWindowWidth; } //返回視窗寬度的函式
         intGetWindowHeight() { return m_nWindowHeight; } //返回視窗高度的函式
         boolIsBackgroundUsed() { return m_bIsBackgroundUsed; }     //返回背景是否在使用的bool值的函式
         voidSetWindowSize(int w, int h) { m_nWindowWidth = w; m_nWindowHeight = h; }  //設定視窗寬度和高度的函式
 
         LPD3DXFONTGetFont(int id)  //返回字型ID函式
         {
                   if(id< 0 || id >= m_nTotalFontNum) return NULL;
                   returnm_pFonts[id];
         }
 
         GUICONTROL*GetGUIControl(int id)  //返回GUI控制元件ID函式
         {
                   if(id< 0 || id >= m_nTotalControlNum) return NULL;
                   return&m_pControls[id];
         }
 
         LPDIRECT3DVERTEXBUFFER9GetVertexBuffer(int id) //返回頂點快取ID函式
         {
                   if(id< 0 || id >= m_nTotalBufferNum) return NULL;
                   returnm_pVertexBuffer[id];
         }
 
 
         boolCreateTextFont(wchar_t *fontName, int size, int *fontID);  //字型建立函式
         boolAddBackground(wchar_t *fileName);  //GUI背景新增函式
         boolAddStaticText(int id, wchar_t *text, float x, float y, unsigned long color, intfontID); //新增靜態文字函式
         boolAddButton(int id, float x, float y, wchar_t *up, wchar_t *over, wchar_t *down);//新增按鈕函式
         voidClearUp( ); //資源清理函式
 
 
};
 
void ProcessGUI(D3DGUIClass *gui, boolLMBDown, int mouseX, int mouseY,
         void(*funcPtr)(intid, int state));  //回撥函式



具體的實現細節如果現在講會有一定的篇幅,我們留待下次講解吧。

四、GUI系統類的使用

接下來,一起來研究研究如果要在我們之前的demo框架裡使用GUI系統的話,需要新增哪些程式碼。

第一步,依舊是新增一些必要的全域性變數:

[cpp] view plaincopyprint?
  1. D3DGUIClass      g_gui= NULL;       //建立GUI類物件
  2. int          g_FontID = -1;         //  GUI中字型物件的ID
  3. bool             g_LMBDown= false;      // GUI中的滑鼠狀態資訊,滑鼠左鍵是否按下的標識
  4. int              g_MouseX= 0, g_MouseY = 0;   //儲存滑鼠座標的兩個變數
D3DGUIClass      g_gui= NULL;  		//建立GUI類物件
int 		 g_FontID = -1;      	//  GUI中字型物件的ID
bool             g_LMBDown= false;      // GUI中的滑鼠狀態資訊,滑鼠左鍵是否按下的標識
int              g_MouseX= 0, g_MouseY = 0;   //儲存滑鼠座標的兩個變數

可以看到,在這裡我們定義了自己寫的GUI系統類D3DGUIClass的類物件,然後是滑鼠狀態資訊相關的變數和一個字型物件。

第二步,在訊息處理函式的switch中新增一些新的case:

[cpp] view plaincopyprint?
  1. //-----------------------------------【WndProc( )函式】--------------------------------------
  2. //      描述:視窗過程函式WndProc,對視窗訊息進行處理
  3. //------------------------------------------------------------------------------------------------
  4. LRESULT CALLBACK WndProc( HWND hwnd, UINTmessage, WPARAM wParam, LPARAM lParam )  //視窗過程函式WndProc
  5. {  
  6.          switch(message )                               //switch語句開始
  7.          {  
  8.          caseWM_PAINT:                                          // 客戶區重繪訊息
  9.                    Direct3D_Render(hwnd,0.0f);          //呼叫Direct3D_Render函式,進行畫面的繪製
  10.                    ValidateRect(hwnd,NULL);   // 更新客戶區的顯示
  11.                    break;                                                                                 //跳出該switch語句
  12.          caseWM_KEYDOWN:                // 鍵盤按下訊息
  13.                    if(wParam == VK_ESCAPE)    // ESC鍵
  14.                             DestroyWindow(hwnd);    // 銷燬視窗, 併發送一條WM_DESTROY訊息
  15.                    break;  
  16.          caseWM_DESTROY:                                    //視窗銷燬訊息
  17.                    Direct3D_CleanUp();     //呼叫Direct3D_CleanUp函式,清理COM介面物件
  18.                    PostQuitMessage(0 );              //向系統表明有個執行緒有終止請求。用來響應WM_DESTROY訊息
  19.                    break;                                                     //跳出該switch語句
  20.          caseWM_KEYUP:  
  21.                    if(wParam== VK_ESCAPE) PostQuitMessage(0);  
  22.                    break;  
  23.          caseWM_LBUTTONDOWN:  
  24.                    g_LMBDown= true;  
  25.                    break;  
  26.          caseWM_LBUTTONUP:  
  27.                    g_LMBDown= false;  
  28.                    break;  
  29.          caseWM_MOUSEMOVE:  
  30.                    g_MouseX= LOWORD (lParam);  
  31.                    g_MouseY= HIWORD (lParam);  
  32.                    break;  
  33.          default:                                                   //若上述case條件都不符合,則執行該default語句
  34.                    returnDefWindowProc( hwnd, message, wParam, lParam );                  //呼叫預設的視窗過程來為應用程式沒有處理的視窗訊息提供預設的處理。
  35.          }  
  36.          return0;                                       //正常退出
  37. }  
//-----------------------------------【WndProc( )函式】--------------------------------------
//      描述:視窗過程函式WndProc,對視窗訊息進行處理
//------------------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hwnd, UINTmessage, WPARAM wParam, LPARAM lParam )  //視窗過程函式WndProc
{
         switch(message )                               //switch語句開始
         {
         caseWM_PAINT:                                          // 客戶區重繪訊息
                   Direct3D_Render(hwnd,0.0f);          //呼叫Direct3D_Render函式,進行畫面的繪製
                   ValidateRect(hwnd,NULL);   // 更新客戶區的顯示
                   break;                                                                                 //跳出該switch語句
 
         caseWM_KEYDOWN:                // 鍵盤按下訊息
                   if(wParam == VK_ESCAPE)    // ESC鍵
                            DestroyWindow(hwnd);    // 銷燬視窗, 併發送一條WM_DESTROY訊息
                   break;
         caseWM_DESTROY:                                    //視窗銷燬訊息
                   Direct3D_CleanUp();     //呼叫Direct3D_CleanUp函式,清理COM介面物件
                   PostQuitMessage(0 );              //向系統表明有個執行緒有終止請求。用來響應WM_DESTROY訊息
                   break;                                                     //跳出該switch語句
 
         caseWM_KEYUP:
                   if(wParam== VK_ESCAPE) PostQuitMessage(0);
                   break;
 
         caseWM_LBUTTONDOWN:
                   g_LMBDown= true;
                   break;
 
         caseWM_LBUTTONUP:
                   g_LMBDown= false;
                   break;
 
         caseWM_MOUSEMOVE:
                   g_MouseX= LOWORD (lParam);
                   g_MouseY= HIWORD (lParam);
                   break;
 
 
         default:                                                   //若上述case條件都不符合,則執行該default語句
                   returnDefWindowProc( hwnd, message, wParam, lParam );                  //呼叫預設的視窗過程來為應用程式沒有處理的視窗訊息提供預設的處理。
         }
 
         return0;                                       //正常退出
}

其實這一步就是在之前訊息的基礎上,添加了左鍵按下,左鍵彈起,和滑鼠移動的訊息響應。

第三步,在進行渲染資源準備的Object_Init( )函式中,新增載入GUI系統中的資源到記憶體中的相關程式碼:

[cpp] view plaincopyprint?
  1. //-----------------------------------【Object_Init()函式】--------------------------------------
  2. //      描述:渲染資源初始化函式,在此函式中進行要被渲染的物體的資源的初始化
  3. //--------------------------------------------------------------------------------------------------
  4. HRESULT Objects_Init()  
  5. {  
  6.          //建立字型
  7.          D3DXCreateFont(g_pd3dDevice,36, 0, 0, 1000, false, DEFAULT_CHARSET,  
  8.                    OUT_DEFAULT_PRECIS,DEFAULT_QUALITY, 0, _T("Calibri"), &g_pTextFPS);  
  9.          D3DXCreateFont(g_pd3dDevice,20, 0, 1000, 0, false, DEFAULT_CHARSET,  
  10.                    OUT_DEFAULT_PRECIS,DEFAULT_QUALITY, 0, L"華文中宋", &g_pTextAdaperName);  
  11.          D3DXCreateFont(g_pd3dDevice,23, 0, 1000, 0, false, DEFAULT_CHARSET,  
  12.                    OUT_DEFAULT_PRECIS,DEFAULT_QUALITY, 0, L"微軟雅黑", &g_pTextHelper);  
  13.          D3DXCreateFont(g_pd3dDevice,26, 0, 1000, 0, false, DEFAULT_CHARSET,  
  14.                    OUT_DEFAULT_PRECIS,DEFAULT_QUALITY, 0, L"黑體", &g_pTextInfor);  
  15.          //設定紋理取樣引數
  16.          g_pd3dDevice->SetSamplerState(0,D3DSAMP_MINFILTER, D3DTEXF_NONE);  
  17.          g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAGFILTER, D3DTEXF_NONE);  
  18.          g_pd3dDevice->SetSamplerState(0,D3DSAMP_MIPFILTER, D3DTEXF_NONE);  
  19. //-----------------------------------【GUI系統相關程式碼】-------------------------------
  20.          //建立GUI系統
  21.          g_gui= new D3DGUIClass(g_pd3dDevice, WINDOW_WIDTH, WINDOW_HEIGHT);  
  22.          if(!g_gui)returnfalse;  
  23.          //新增背景圖片
  24.          if(!g_gui->AddBackground(L"GameMedia\\Assassinscreed.jpg")) returnfalse;  
  25.          //新增字型
  26.          if(!g_gui->CreateTextFont(L"微軟雅黑",28, &g_FontID)) returnfalse;  
  27.          //新增靜態文字到GUI中
  28.          if(!g_gui->AddStaticText(STATIC_ID_1,L"Version 淺墨1.0版",  
  29.                    1170,735, D3DCOLOR_XRGB(55,155,255), g_FontID)) returnfalse;  
  30.          if(!g_gui->AddStaticText(STATIC_ID_2,L