您好,登錄后才能下訂單哦!
本系列文章由zhmxy555(毛星云)編寫,轉載請注明出處。
作者:毛星云(淺墨) 郵箱: happylifemxy@163.com
眾所周知,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的介紹。
HUD,heads-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結構體的指針對象
//檢查鼠標是否懸停或者點擊了按鈕 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系統類的實現細節留待下篇講解(如果需要額外用一次更新來講解的話)。
還是把詳細注釋的頭文件貼一下吧:
//==================================================== // 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系統的話,需要添加哪些代碼。
第一步,依舊是添加一些必要的全局變量:
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:
//-----------------------------------【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系統中的資源到內存中的相關代碼:
//-----------------------------------【Object_Init()函數】-------------------------------------- // 描述:渲染資源初始化函數,在此函數中進行要被渲染的物體的資源的初始化 //-------------------------------------------------------------------------------------------------- HRESULT Objects_Init() { //創建字體 D3DXCreateFont(g_pd3dDevice,36, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,DEFAULT_QUALITY, 0, _T("Calibri"), &g_pTextFPS); D3DXCreateFont(g_pd3dDevice,20, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,DEFAULT_QUALITY, 0, L"華文中宋", &g_pTextAdaperName); D3DXCreateFont(g_pd3dDevice,23, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,DEFAULT_QUALITY, 0, L"微軟雅黑", &g_pTextHelper); D3DXCreateFont(g_pd3dDevice,26, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,DEFAULT_QUALITY, 0, L"黑體", &g_pTextInfor); //設置紋理采樣參數 g_pd3dDevice->SetSamplerState(0,D3DSAMP_MINFILTER, D3DTEXF_NONE); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAGFILTER, D3DTEXF_NONE); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MIPFILTER, D3DTEXF_NONE); //-----------------------------------【GUI系統相關代碼】------------------------------- //創建GUI系統 g_gui= new D3DGUIClass(g_pd3dDevice, WINDOW_WIDTH, WINDOW_HEIGHT); if(!g_gui)return false; //添加背景圖片 if(!g_gui->AddBackground(L"GameMedia\\Assassinscreed.jpg")) return false; //添加字體 if(!g_gui->CreateTextFont(L"微軟雅黑",28, &g_FontID)) return false; //添加靜態文本到GUI中 if(!g_gui->AddStaticText(STATIC_ID_1,L"Version 淺墨1.0版", 1170,735, D3DCOLOR_XRGB(55,155,255), g_FontID)) return false; if(!g_gui->AddStaticText(STATIC_ID_2,L"淺墨DirectX教程第三季之 打造游戲GUI界面", 500,10, D3DCOLOR_XRGB(255,255,255), g_FontID)) return false; //添加4個按鈕,分別是開始游戲,載入進度,選項和退出游戲,每個按鈕對應3幅圖 if(!g_gui->AddButton(BUTTON_ID_1,650, 340, L"GameMedia\\startUp.png", L"GameMedia\\StartOver.png",L"GameMedia\\startDown.png")) return false; if(!g_gui->AddButton(BUTTON_ID_2,650, 385, L"GameMedia\\loadUp.png", L"GameMedia\\loadOver.png",L"GameMedia\\loadDown.png")) return false; if(!g_gui->AddButton(BUTTON_ID_3,650, 430, L"GameMedia\\optionsUp.png", L"GameMedia\\optionsOver.png",L"GameMedia\\optionsDown.png")) return false; if(!g_gui->AddButton(BUTTON_ID_4,650, 475, L"GameMedia\\quitUp.png", L"GameMedia\\quitOver.png",L"GameMedia\\quitDown.png")) return false; returnS_OK; }
代碼中都注釋的比較詳細,就不多說明了哈。
第四步,添加一個GUI系統的回調函數:
void GUICallback(int id, int state) { switch(id) { caseBUTTON_ID_1: //“開始游戲”按鈕的相關代碼實現 break; caseBUTTON_ID_2: //“載入游戲”按鈕的相關代碼實現 break; caseBUTTON_ID_3: //“選項”按鈕的相關代碼實現 break; caseBUTTON_ID_4: //如果點擊了退出按鈕,就退出程序 if(state== UGP_BUTTON_DOWN) PostQuitMessage(0); break; } }
這里是一個switch—case語句總領的回調函數。
大家也許暫時會對這個函數的使用不太理解,其實它和我們自定義的ProcessGUI有著千絲萬縷的聯系,具體內容我們在出GUI的第二篇文章的時候再詳細講解。這里我們只要知道它是和ProcessGUI搞基的就可以了。
舉個例子吧,對其中的BUTTON_ID_1按鈕,就是指的GUI界面中的“Start Game”按鈕,而點擊之后的余下響應代碼(也就是消息響應代碼)就寫在這個case之后,比如說點擊了新游戲的開始后需要渲染游戲畫面等等一系列代碼。
第五步,在渲染五步曲的第三步中調用一個封裝好功能的ProcessGUI函數就可以了。這個函數的具體實現我們在稍后退出的文章中會講到。
//-------------------------------------------------------------------------------------- //【Direct3D渲染五步曲之二】:開始繪制 //-------------------------------------------------------------------------------------- g_pd3dDevice->BeginScene(); // 開始繪制 //-------------------------------------------------------------------------------------- //【Direct3D渲染五步曲之三】:正式繪制 //-------------------------------------------------------------------------------------- //處理和渲染GUI系統 ProcessGUI(g_gui,g_LMBDown, g_MouseX, g_MouseY, GUICallback); //-----------------------------【繪制文字信息】----------------------------- HelpText_Render(hwnd); //-------------------------------------------------------------------------------------- //【Direct3D渲染五步曲之四】:結束繪制 //-------------------------------------------------------------------------------------- g_pd3dDevice->EndScene(); // 結束繪制
五、詳細注釋的源代碼欣賞
這次的工程因為是作為GUI的初步演示,自然就有了孑然一身的感覺,除了main.cpp和D3DUtil.h就是D3DGUIClass類的源文件和頭文件了。如下圖:
程序主要是實現了簡單的GUI系統,通過GUI系統的功能在屏幕上添加了游戲菜單的四個按鈕,并且在屏幕上輸出了“淺墨DirectX教程第三季 之 打造游戲GUI界面”,"Version 淺墨1.0版"這兩段文字。
而且目前對“quit”退出按鈕的功能進行了實行實現,其實就簡單一句if(state == UGP_BUTTON_DOWN) PostQuitMessage(0);。。。。。
那么,老規矩,上程序的核心部分,main函數的代碼吧:
//-----------------------------------【程序說明】---------------------------------------------- // 【Visual C++】游戲開發系列配套源碼五十六 淺墨DirectX教程二十三 打造游戲GUI界面(一) // VS2010版 // 2013年11月 Create by 淺墨 // 背景音樂素材出處: 刺客信條 //------------------------------------------------------------------------------------------------ //-----------------------------------【宏定義部分】-------------------------------------------- // 描述:定義一些輔助宏 //------------------------------------------------------------------------------------------------ #define WINDOW_WIDTH 1366 //為窗口寬度定義的宏,以方便在此處修改窗口寬度 #define WINDOW_HEIGHT 768 //為窗口高度定義的宏,以方便在此處修改窗口高度 #define WINDOW_TITLE _T("【致我們永不熄滅的游戲開發夢想】 淺墨DirectX教程二十三 打造游戲GUI界面(一)博文配套示例程序 by淺墨") //為窗口標題定義的宏 //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //------------------------------------------------------------------------------------------------ #include <d3d9.h> #include <d3dx9.h> #include <tchar.h> #include <time.h> #include "D3DUtil.h" #include "D3DGUIClass.h" //-----------------------------------【庫文件包含部分】--------------------------------------- // 描述:包含程序所依賴的庫文件 //------------------------------------------------------------------------------------------------ #pragma comment(lib,"d3d9.lib") #pragma comment(lib,"d3dx9.lib") #pragma comment(lib, "dinput8.lib") // 使用DirectInput必須包含的庫文件,注意這里有8 #pragma comment(lib,"dxguid.lib") #pragma comment(lib, "winmm.lib") // 地板的頂點結構 struct CUSTOMVERTEX { FLOAT _x, _y, _z; FLOAT _u, _v ; CUSTOMVERTEX(FLOAT x, FLOAT y, FLOAT z, FLOAT u, FLOAT v) : _x(x), _y(y), _z(z), _u(u), _v(v) {} }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX1) //-----------------------------------【全局變量聲明部分】------------------------------------- // 描述:全局變量的聲明 //------------------------------------------------------------------------------------------------ LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; //Direct3D設備對象 LPD3DXFONT g_pTextFPS =NULL; //字體COM接口 LPD3DXFONT g_pTextAdaperName = NULL; // 顯卡信息的2D文本 LPD3DXFONT g_pTextHelper = NULL; // 幫助信息的2D文本 LPD3DXFONT g_pTextInfor= NULL; // 繪制信息的2D文本 float g_FPS= 0.0f; //一個浮點型的變量,代表幀速率 wchar_t g_strFPS[50] ={0}; //包含幀速率的字符數組 wchar_t g_strAdapterName[60] ={0}; //包含顯卡名稱的字符數組 D3DGUIClass *g_gui = NULL; //創建GUI類對象 bool g_LMBDown = false; // GUI中的鼠標狀態信息,鼠標左鍵是否按下的標識 int g_MouseX = 0, g_MouseY = 0; //存儲鼠標坐標的兩個變量 int g_FontID = -1; // GUI中字體對象的ID //-----------------------------------【全局函數聲明部分】------------------------------------- // 描述:全局函數聲明,防止“未聲明的標識”系列錯誤 //------------------------------------------------------------------------------------------------ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ); HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance); HRESULT Objects_Init(); void Direct3D_Render( HWND hwnd,FLOAT fTimeDelta); void Direct3D_Update( HWND hwnd,FLOAT fTimeDelta); void Direct3D_CleanUp( ); float Get_FPS(); void HelpText_Render(HWND hwnd); void GUICallback(int id, int state); //-----------------------------------【WinMain( )函數】-------------------------------------- // 描述:Windows應用程序的入口函數,我們的程序從這里開始 //------------------------------------------------------------------------------------------------ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd) { //開始設計一個完整的窗口類 WNDCLASSEX wndClass={0} ; //用WINDCLASSEX定義了一個窗口類,即用wndClass實例化了WINDCLASSEX,用于之后窗口的各項初始化 wndClass.cbSize = sizeof( WNDCLASSEX ) ; //設置結構體的字節數大小 wndClass.style = CS_HREDRAW | CS_VREDRAW; //設置窗口的樣式 wndClass.lpfnWndProc = WndProc; //設置指向窗口過程函數的指針 wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; //指定包含窗口過程的程序的實例句柄。 wndClass.hIcon=(HICON)::LoadImage(NULL,_T("GameMedia\\icon.ico"),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //從全局的::LoadImage函數從本地加載自定義ico圖標 wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口類的光標句柄。 wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //為hbrBackground成員指定一個灰色畫刷句柄 wndClass.lpszMenuName = NULL; //用一個以空終止的字符串,指定菜單資源的名字。 wndClass.lpszClassName = _T("ForTheDreamOfGameDevelop"); //用一個以空終止的字符串,指定窗口類的名字。 if( !RegisterClassEx( &wndClass ) ) //設計完窗口后,需要對窗口類進行注冊,這樣才能創建該類型的窗口 return -1; HWND hwnd = CreateWindow( _T("ForTheDreamOfGameDevelop"),WINDOW_TITLE, //喜聞樂見的創建窗口函數CreateWindow WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL ); //Direct3D資源的初始化,調用失敗用messagebox予以顯示 if (!(S_OK==Direct3D_Init (hwnd,hInstance))) { MessageBox(hwnd, _T("Direct3D初始化失敗~!"), _T("淺墨的消息窗口"), 0); //使用MessageBox函數,創建一個消息窗口 } PlaySound(L"GameMedia\\Heart - 刺客信條.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循環播放背景音樂 MoveWindow(hwnd,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,true); //調整窗口顯示時的位置,窗口左上角位于屏幕坐標(200,10)處 ShowWindow( hwnd, nShowCmd ); //調用Win32函數ShowWindow來顯示窗口 UpdateWindow(hwnd); //對窗口進行更新,就像我們買了新房子要裝修一樣 //消息循環過程 MSG msg = { 0 }; //初始化msg while( msg.message != WM_QUIT ) //使用while循環 { static FLOAT fLastTime = (float)::timeGetTime(); static FLOAT fCurrTime = (float)::timeGetTime(); static FLOAT fTimeDelta = 0.0f; fCurrTime = (float)::timeGetTime(); fTimeDelta = (fCurrTime - fLastTime) / 1000.0f; fLastTime = fCurrTime; if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) //查看應用程序消息隊列,有消息時將隊列中的消息派發出去。 { TranslateMessage( &msg ); //將虛擬鍵消息轉換為字符消息 DispatchMessage( &msg ); //該函數分發一個消息給窗口程序。 } else { Direct3D_Update(hwnd,fTimeDelta); //調用更新函數,進行畫面的更新 Direct3D_Render(hwnd,fTimeDelta); //調用渲染函數,進行畫面的渲染 } } UnregisterClass(_T("ForTheDreamOfGameDevelop"), wndClass.hInstance); return 0; } //-----------------------------------【WndProc( )函數】-------------------------------------- // 描述:窗口過程函數WndProc,對窗口消息進行處理 //------------------------------------------------------------------------------------------------ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) //窗口過程函數WndProc { switch( message ) //switch語句開始 { case WM_PAINT: // 客戶區重繪消息 Direct3D_Render(hwnd,0.0f); //調用Direct3D_Render函數,進行畫面的繪制 ValidateRect(hwnd, NULL); // 更新客戶區的顯示 break; //跳出該switch語句 case WM_KEYDOWN: // 鍵盤按下消息 if (wParam == VK_ESCAPE) // ESC鍵 DestroyWindow(hwnd); // 銷毀窗口, 并發送一條WM_DESTROY消息 break; case WM_DESTROY: //窗口銷毀消息 Direct3D_CleanUp(); //調用Direct3D_CleanUp函數,清理COM接口對象 PostQuitMessage( 0 ); //向系統表明有個線程有終止請求。用來響應WM_DESTROY消息 break; //跳出該switch語句 case WM_KEYUP: if(wParam == VK_ESCAPE) PostQuitMessage(0); break; case WM_LBUTTONDOWN: g_LMBDown = true; break; case WM_LBUTTONUP: g_LMBDown = false; break; case WM_MOUSEMOVE: g_MouseX = LOWORD (lParam); g_MouseY = HIWORD (lParam); break; default: //若上述case條件都不符合,則執行該default語句 return DefWindowProc( hwnd, message, wParam, lParam ); //調用缺省的窗口過程來為應用程序沒有處理的窗口消息提供缺省的處理。 } return 0; //正常退出 } //-----------------------------------【Direct3D_Init( )函數】---------------------------------- // 描述:Direct3D初始化函數,進行Direct3D的初始化 //------------------------------------------------------------------------------------------------ HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance) { //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之一,創接口】:創建Direct3D接口對象, 以便用該Direct3D對象創建Direct3D設備對象 //-------------------------------------------------------------------------------------- LPDIRECT3D9 pD3D = NULL; //Direct3D接口對象的創建 if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口對象,并進行DirectX版本協商 return E_FAIL; //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之二,取信息】:獲取硬件設備信息 //-------------------------------------------------------------------------------------- D3DCAPS9 caps; int vp = 0; if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) ) { return E_FAIL; } if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ) vp = D3DCREATE_HARDWARE_VERTEXPROCESSING; //支持硬件頂點運算,我們就采用硬件頂點運算,妥妥的 else vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件頂點運算,無奈只好采用軟件頂點運算 //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之三,填內容】:填充D3DPRESENT_PARAMETERS結構體 //-------------------------------------------------------------------------------------- D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(&d3dpp, sizeof(d3dpp)); d3dpp.BackBufferWidth = WINDOW_WIDTH; d3dpp.BackBufferHeight = WINDOW_HEIGHT; d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; d3dpp.BackBufferCount = 2; d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE; d3dpp.MultiSampleQuality = 0; d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hwnd; d3dpp.Windowed = true; d3dpp.EnableAutoDepthStencil = true; d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; d3dpp.Flags = 0; d3dpp.FullScreen_RefreshRateInHz = 0; d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之四,創設備】:創建Direct3D設備接口 //-------------------------------------------------------------------------------------- if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, vp, &d3dpp, &g_pd3dDevice))) return E_FAIL; //獲取顯卡信息到g_strAdapterName中,并在顯卡名稱之前加上“當前顯卡型號:”字符串 wchar_t TempName[60]=L"當前顯卡型號:"; //定義一個臨時字符串,且方便了把"當前顯卡型號:"字符串引入我們的目的字符串中 D3DADAPTER_IDENTIFIER9 Adapter; //定義一個D3DADAPTER_IDENTIFIER9結構體,用于存儲顯卡信息 pD3D->GetAdapterIdentifier(0,0,&Adapter);//調用GetAdapterIdentifier,獲取顯卡信息 int len = MultiByteToWideChar(CP_ACP,0, Adapter.Description, -1, NULL, 0);//顯卡名稱現在已經在Adapter.Description中了,但是其為char類型,我們要將其轉為wchar_t類型 MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, g_strAdapterName, len);//這步操作完成后,g_strAdapterName中就為當前我們的顯卡類型名的wchar_t型字符串了 wcscat_s(TempName,g_strAdapterName);//把當前我們的顯卡名加到“當前顯卡型號:”字符串后面,結果存在TempName中 wcscpy_s(g_strAdapterName,TempName);//把TempName中的結果拷貝到全局變量g_strAdapterName中,大功告成~ if(!(S_OK==Objects_Init())) return E_FAIL; SAFE_RELEASE(pD3D) //LPDIRECT3D9接口對象的使命完成,我們將其釋放掉 return S_OK; } //-----------------------------------【Object_Init( )函數】-------------------------------------- // 描述:渲染資源初始化函數,在此函數中進行要被渲染的物體的資源的初始化 //-------------------------------------------------------------------------------------------------- HRESULT Objects_Init() { //創建字體 D3DXCreateFont(g_pd3dDevice, 36, 0, 0, 1000, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("Calibri"), &g_pTextFPS); D3DXCreateFont(g_pd3dDevice, 20, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"華文中宋", &g_pTextAdaperName); D3DXCreateFont(g_pd3dDevice, 23, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"微軟雅黑", &g_pTextHelper); D3DXCreateFont(g_pd3dDevice, 26, 0, 1000, 0, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, L"黑體", &g_pTextInfor); //設置紋理采樣參數 g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_NONE); g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_NONE); g_pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); //-----------------------------------【GUI系統相關代碼】------------------------------- // 創建GUI系統 g_gui = new D3DGUIClass(g_pd3dDevice, WINDOW_WIDTH, WINDOW_HEIGHT); if(!g_gui) return false; // 添加背景圖片 if(!g_gui->AddBackground(L"GameMedia\\Assassins creed.jpg")) return false; // 添加字體 if(!g_gui->CreateTextFont(L"微軟雅黑", 28, &g_FontID)) return false; // 添加靜態文本到GUI中 if(!g_gui->AddStaticText(STATIC_ID_1, L"Version 淺墨1.0版", 1170, 735, D3DCOLOR_XRGB(55,155,255), g_FontID)) return false; if(!g_gui->AddStaticText(STATIC_ID_2, L"淺墨DirectX教程第三季 之 打造游戲GUI界面", 500, 10, D3DCOLOR_XRGB(255,255,255), g_FontID)) return false; // 添加4個按鈕,分別是開始游戲,載入進度,選項和退出游戲,每個按鈕對應3幅圖 if(!g_gui->AddButton(BUTTON_ID_1, 650, 340, L"GameMedia\\startUp.png", L"GameMedia\\StartOver.png", L"GameMedia\\startDown.png")) return false; if(!g_gui->AddButton(BUTTON_ID_2, 650, 385, L"GameMedia\\loadUp.png", L"GameMedia\\loadOver.png", L"GameMedia\\loadDown.png")) return false; if(!g_gui->AddButton(BUTTON_ID_3, 650, 430, L"GameMedia\\optionsUp.png", L"GameMedia\\optionsOver.png", L"GameMedia\\optionsDown.png")) return false; if(!g_gui->AddButton(BUTTON_ID_4, 650, 475, L"GameMedia\\quitUp.png", L"GameMedia\\quitOver.png", L"GameMedia\\quitDown.png")) return false; return S_OK; } void GUICallback(int id, int state) { switch(id) { case BUTTON_ID_1: //“開始游戲”按鈕的相關代碼實現 break; case BUTTON_ID_2: //“載入游戲”按鈕的相關代碼實現 break; case BUTTON_ID_3: //“選項”按鈕的相關代碼實現 break; case BUTTON_ID_4: //如果點擊了退出按鈕,就退出程序 if(state == UGP_BUTTON_DOWN) PostQuitMessage(0); break; } } //-----------------------------------【Direct3D_Update( )函數】-------------------------------- // 描述:不是即時渲染代碼但是需要即時調用的,如按鍵后的坐標的更改,都放在這里 //-------------------------------------------------------------------------------------------------- void Direct3D_Update( HWND hwnd,FLOAT fTimeDelta) { //GUI的實現暫時不需要在這里寫代碼 } //-----------------------------------【Direct3D_Render( )函數】------------------------------- // 描述:使用Direct3D進行渲染 //-------------------------------------------------------------------------------------------------- void Direct3D_Render(HWND hwnd,FLOAT fTimeDelta) { //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之一】:清屏操作 //-------------------------------------------------------------------------------------- g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL, D3DCOLOR_XRGB(100, 255, 255), 1.0f, 0); //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之二】:開始繪制 //-------------------------------------------------------------------------------------- g_pd3dDevice->BeginScene(); // 開始繪制 //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之三】:正式繪制 //-------------------------------------------------------------------------------------- // 處理和渲染GUI系統 ProcessGUI(g_gui, g_LMBDown, g_MouseX, g_MouseY, GUICallback); //-----------------------------【繪制文字信息】----------------------------- HelpText_Render(hwnd); //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之四】:結束繪制 //-------------------------------------------------------------------------------------- g_pd3dDevice->EndScene(); // 結束繪制 //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之五】:顯示翻轉 //-------------------------------------------------------------------------------------- g_pd3dDevice->Present(NULL, NULL, NULL, NULL); // 翻轉與顯示 } //-----------------------------------【HelpText_Render( )函數】------------------------------- // 描述:封裝了幫助信息的函數 //-------------------------------------------------------------------------------------------------- void HelpText_Render(HWND hwnd) { //定義一個矩形,用于獲取主窗口矩形 RECT formatRect; GetClientRect(hwnd, &formatRect); //在窗口右上角處,顯示每秒幀數 formatRect.top = 5; int charCount = swprintf_s(g_strFPS, 20, _T("FPS:%0.3f"), Get_FPS() ); g_pTextFPS->DrawText(NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_RGBA(0,239,136,255)); //顯示顯卡類型名 g_pTextAdaperName->DrawText(NULL,g_strAdapterName, -1, &formatRect, DT_TOP | DT_LEFT, D3DXCOLOR(1.0f, 0.5f, 0.0f, 1.0f)); } //-----------------------------------【Get_FPS( )函數】------------------------------------------ // 描述:用于計算每秒幀速率的一個函數 //-------------------------------------------------------------------------------------------------- float Get_FPS() { //定義四個靜態變量 static float fps = 0; //我們需要計算的FPS值 static int frameCount = 0;//幀數 static float currentTime =0.0f;//當前時間 static float lastTime = 0.0f;//持續時間 frameCount++;//每調用一次Get_FPS()函數,幀數自增1 currentTime = timeGetTime()*0.001f;//獲取系統時間,其中timeGetTime函數返回的是以毫秒為單位的系統時間,所以需要乘以0.001,得到單位為秒的時間 //如果當前時間減去持續時間大于了1秒鐘,就進行一次FPS的計算和持續時間的更新,并將幀數值清零 if(currentTime - lastTime > 1.0f) //將時間控制在1秒鐘 { fps = (float)frameCount /(currentTime - lastTime);//計算這1秒鐘的FPS值 lastTime = currentTime; //將當前時間currentTime賦給持續時間lastTime,作為下一秒的基準時間 frameCount = 0;//將本次幀數frameCount值清零 } return fps; } //-----------------------------------【Direct3D_CleanUp( )函數】-------------------------------- // 描述:對Direct3D的資源進行清理,釋放COM接口對象 //--------------------------------------------------------------------------------------------------- void Direct3D_CleanUp() { //釋放COM接口對象 SAFE_RELEASE(g_pd3dDevice); SAFE_RELEASE(g_pTextFPS) SAFE_RELEASE(g_pd3dDevice) SAFE_DELETE(g_gui) }
那么,最后一起看看運行截圖吧:
鼠標懸停在options按鈕之上,可以發現按鈕“下陷”了
點擊quit按鈕,游戲程序便會退出:
嗯,本篇文章到這里就基本結束了,最后放出本篇文章配套示例程序的下載地址。
本篇文章的配套源代碼請點擊這里下載:
【淺墨DirectX提高班】配套源代碼之二十三下載 (CSDN下載頻道)
【淺墨DirectX提高班】配套源代碼之二十三下載 (百度云盤)
文章最后,依舊是送大家一些正能量,不過今天有所不同,它們是:
Seven Things I Would Really Tresure In Life(生命中我將珍視的七個習慣):
1) 對某件事(某種物)持久的熱愛和堅持。
2)早晨起來寫下今天要做的三件事,晚上入睡前能真的將它們做完。
3)永遠對未知和未見的充滿著好奇,當它們出現時毫不猶豫地去擁抱它們。
4)并不因他人的非議或外界的阻力而改變對自己的看法的堅持,當要被迫使違背心愿去做某事時堅定不移地說"不"。
5)活在當下,珍視已有的一切,someday you will miss today.
6)對和自己完全無關的人,僅因其所處的境遇而產生同情與關心,并盡己所能去幫助。
7)對一個更好的世界和一個更好的自己的向往與不放下的執念。
是的,塑造自己的過程會很疼,但是最終你能收獲一個更好的自己。加油:)
下周一,讓我們離游戲開發的夢想更近一步。
下周一,游戲開發筆記,我們,不見不散。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。