您好,登錄后才能下訂單哦!
本系列文章由zhmxy555(毛星云)編寫,轉(zhuǎn)載請注明出處。
作者:毛星云(淺墨) 郵箱: happylifemxy@163.com
眾所周知,GUI是游戲中不可缺少的元素,這篇文章中,我們首先了解了游戲GUI界面的知識與相關(guān)概念,然后一起設(shè)計了一個封裝好GUI圖形界面的C++類。這個類有著非常強(qiáng)的擴(kuò)展性,使用也是極其方便,很適合二次開發(fā)。
先看一張實(shí)現(xiàn)的效果圖吧:
其中的背景音樂,游戲圖標(biāo)和背景圖片都出自育碧公司的招牌式大作《刺客信條》。
程序的窗口大小已經(jīng)被淺墨調(diào)成了1366 x768,現(xiàn)階段比較流行的筆記本分辨率尺寸(其實(shí)真的想吐槽這個奇葩萬年不變的電腦屏幕分辨率,在如今的后PC時代。手機(jī)屏幕都開始對1920 x1080的分辨率不滿足了。。。。)
嗯,開始正文吧。
一、UI和GUI的概述
首先我們看一下UI的比較正統(tǒng)的定義:用戶界面(User Interface,簡稱UI,亦稱使用者界面)是系統(tǒng)和用戶之間進(jìn)行交互和信息交換的媒介,它實(shí)現(xiàn)信息的內(nèi)部形式與人類可以接受形式之間的轉(zhuǎn)換。
用戶界面是介于用戶與硬件之間,為彼此之間交互溝通而設(shè)計的相關(guān)軟件,使得用戶能夠方便有效地去操作硬件以達(dá)成雙向之交互,完成所希望的工作,用戶界面定義廣泛,包含了人機(jī)交互與圖形用戶界面,凡參與人類與機(jī)械的信息交流的領(lǐng)域都存在著用戶界面。
而GUI的正統(tǒng)定義是:圖形用戶界面(Graphical User Interface,簡稱 GUI,又稱圖形用戶接口)是指采用圖形方式顯示的計算機(jī)操作用戶界面。與早期計算機(jī)使用的命令行界面相比,圖形界面對于用戶來說在視覺上更易于接受。
我們知道,在游戲中,能夠和游戲玩家進(jìn)行交互是至關(guān)重要的。這樣的交流可以是文本形式的,視覺顯示,聲音信號等等。而對于視覺顯示而言,絕大多數(shù)游戲毋庸置疑就是用圖形用戶界面(GUI)來和游戲玩家進(jìn)行交互的。而在游戲過程中,依舊少不了GUI界面的出場,比如說狀態(tài)顯示界面(Head-up Display,HUD),HUD,這個界面提供一切和游戲有關(guān)的信息。用于提供玩家剩余任務(wù)時間,生命值,坐標(biāo)位置,以及更多信息。
讓我們欣賞一部分近期的3A游戲大作的UI界面美圖吧:
《鬼泣5》:
《英雄無敵6》:
欣賞完了,我們繼續(xù)開講吧。
二、關(guān)于狀態(tài)顯示界面(HUD)
這一節(jié)里讓我們一起科普HUD的概念。
我們知道,通常情況下在游戲中屏幕上會有很多的小的界面模塊配上文字,為玩家提供了一些有用的信息,比如玩家剩余任務(wù)時間,生命值,魔法值,坐標(biāo)位置等等更多的信息。而因?yàn)樵谕婕液陀螒蚪换サ倪^程中,這些界面是透過攝像機(jī)視角顯示出來的,它們被稱為HUD(heads-up-display)。在視頻游戲中,HUD就是一類可以實(shí)時向玩家顯示有用信息和符號的GUI,并且它和諸如游戲菜單這樣的其他界面不同.HUD中通常沒有按鈕、編輯框或者適合玩家交互的內(nèi)容,因?yàn)橥婕彝际潜挥螒虮旧淼漠嬅婧蛢?nèi)容所吸引,而不是去關(guān)注HUD。所以呢,HUD通常并不去使用可以交互的元素,而傾向于使用文本和圖像來表示某些信息。所以,游戲選項(xiàng)和偏好設(shè)置這樣的內(nèi)容不是HUD的菜,它們直接交由系統(tǒng)菜單來完成。
下面我們看一看更加規(guī)范化的HUD的介紹。
HUD,heads-up-display,硬著過來翻譯就是“抬頭顯視設(shè)備”- -。這是一個從軍事領(lǐng)域起源的技術(shù),可以把一些重要的戰(zhàn)術(shù)信息顯示在正常觀察方向的視野范圍內(nèi),而同時又不會影響對于環(huán)境的注意,也不用總是轉(zhuǎn)移視線去專門觀察儀表板上的那些指針和數(shù)據(jù)。游戲借鑒了這個概念,把游戲相關(guān)的信息以類似HUD的方式顯示在游戲畫面上,讓玩家可以隨時了解那些最重要最直接相關(guān)的內(nèi)容。當(dāng)然玩家要獲得游戲信息可以有別的方式,比如菜單。菜單有著專門的界面,可以容納更大的信息量,但卻不能和游戲畫面同時出現(xiàn)。調(diào)出菜單意味著中斷游戲流程,HUD則在提供必要的信息的同時完全避免了這個問題。
雖然菜單提供了大量的信息,然而對于處在自由行動狀態(tài)下的玩家而言,這些信息都不是立刻需要獲取的。HUD只提供了最重要最基本的內(nèi)容:當(dāng)前場景的地圖。切換到戰(zhàn)斗狀態(tài)下之后,HUD所提供的信息便會轉(zhuǎn)變?yōu)槟切┲缓蛻?zhàn)斗相關(guān)的部分。
記得最早的游戲pong在設(shè)計的時候就沒有HUD的。對于這樣一個簡單的游戲而言,唯一對玩家有意義需要及時掌握的就是雙方的比分,而最早版本的pong沒有這個功能,玩家需要自己去記錄比分。游戲設(shè)計者很快意識到了這個問題,HUD很快就被整合到了后來的游戲之中,并隨著游戲的進(jìn)化一起演變,完善。
如果被上面的這些書面語繞暈了沒關(guān)系,說了這么多,一言以蔽之:菜單的目的是大而全,HUD的目的則是少而精。HUD主要注重的是實(shí)時向玩家顯示有用信息和符號的GUI,和而系統(tǒng)菜單GUI可以給玩家提供更多更全的信息。
不同類型的游戲玩起來的重點(diǎn)不一樣,HUD在提供的信息方面也有很大的差別。我們不妨按照游戲的類別,來看看各種游戲的HUD設(shè)計的模式和重點(diǎn)。
1.角色扮演類游戲
取決于游戲是否會在行動場景和戰(zhàn)斗場景之間切換,角色扮演游戲的HUD設(shè)計會有所不同,關(guān)于行動的那一部分內(nèi)容,比如地圖和方向指示之類的信息可能會被單獨(dú)分列出來。但總體上角色扮演游戲的HUD信息量基本是一致的:玩家的生命,魔法,行動力,狀態(tài)(或者其它因游戲而異的內(nèi)容)等等的數(shù)值,玩家可以“一鍵”接觸到的物品魔法等等東西,如果物品和魔法的內(nèi)容很復(fù)雜,那么一般都會把全面調(diào)節(jié)的功能交給菜單來完成。比如預(yù)設(shè)快捷鍵和菜單光標(biāo)位置記憶功能就是這樣的目的,把菜單中全面而細(xì)微的調(diào)節(jié)能力中,挑選出一些最重要的,放到HUD上來,共玩家選擇使用。
2.格斗游戲
無論格斗游戲系統(tǒng)本身怎么進(jìn)化,從2D變到3D,格斗游戲的HUD總是保持著自己一貫的特色。格斗游戲的HUD大多分成兩個部分:第一,對戰(zhàn)斗數(shù)據(jù)的統(tǒng)計,第二,對戰(zhàn)斗中精彩場面的描述。前者就是那些顯眼的血槽,還包括時間,局?jǐn)?shù)計分。后者則是對于連擊之類的精彩動作的積分等等信息。其中血槽這個東西是游戲HUD設(shè)計上一個非常典型的東西。
3.體育游戲
體育類游戲在設(shè)計HUD的問題上有著天然的兩個參考坐標(biāo)系:電視轉(zhuǎn)播畫面和球隊(duì)的戰(zhàn)術(shù)分析圖。游戲的HUD結(jié)合了這兩者,即體現(xiàn)出了電視轉(zhuǎn)播畫面的現(xiàn)場感,也做出了戰(zhàn)術(shù)分析圖那樣的清晰感,讓玩家盡可能的在有身臨其境的感覺的同時也對比賽局面有著清晰的了解。實(shí)際上體育類游戲的HUD設(shè)計作的是如此的好,以至于最近以來,很多體育項(xiàng)目的電視轉(zhuǎn)播畫面開始學(xué)習(xí)這類游戲的HUD設(shè)計,往畫面上添加一些即時的比賽信息和戰(zhàn)術(shù)分析。
4.駕駛模擬類游戲
這類游戲所要模仿的對象就是HUD這個概念的來源的地方,所以駕駛模擬類游戲HUD設(shè)計的原則也就變得非常簡單而直接:盡可能的去重現(xiàn)模擬對象的原始HUD就可以了。當(dāng)然根據(jù)游戲模擬真實(shí)的程度不同,再現(xiàn)真實(shí)HUD設(shè)計的程度也有所區(qū)別。HUD的設(shè)計始終存在一個如何抽取最重要的信息提示玩家的問題,過于仿真的游戲HUD設(shè)計將大量的信息不加選擇的堆在玩家面前。對于游戲本身“模擬”這個概念而言,是好事,但對于像通過這些游戲來體驗(yàn)現(xiàn)實(shí)生活中不可能接觸到的東西這個目的而言,高度的仿真模擬往往會成為上手的障礙。游戲畢竟是游戲,游戲的HUD如何在仿真模擬和抽象表現(xiàn)上把握平衡,是這類游戲的一個突出問題。
5.動作射擊類游戲
射擊類游戲的HUD通常都包括了玩家的狀態(tài),玩家的武器狀態(tài),地圖,以及目標(biāo)指示這四個方面。第三人稱的射擊游戲或者團(tuán)隊(duì)策略類射擊游戲在HUD設(shè)計上和傳統(tǒng)的第一人稱射擊游戲區(qū)別也不是很大,重點(diǎn)同樣在這些要素上。HUD的引入給所有的射擊類游戲,無論游戲本身的背景設(shè)定是在什么樣的時代,都帶來了一定的科幻未來的要素。真實(shí)的戰(zhàn)斗中,對于戰(zhàn)場情況的把握從來都是很大的挑戰(zhàn),不斷進(jìn)步的單兵信息化裝備正是力求解決這些問題。射擊類游戲通過HUD的引入超前的解決了這個問題。就目前而言,感覺做的最好的射擊類游戲HUD恰恰就是一些未來題材的射擊游戲,等下在HUD進(jìn)化中我們會看到例子。
6.策略類游戲
最為復(fù)雜的一類游戲HUD,事實(shí)上在這類游戲里面HUD和菜單往往難以截然區(qū)分。因?yàn)橥婕規(guī)缀趺繒r每刻都需要確實(shí)的掌握整個游戲空間里大量單位的行動。HUD是簡化了的菜單,但在很多的策略類游戲上,簡化只是一個美好的愿望,游戲本身的復(fù)雜程度和玩家的“上帝”視角導(dǎo)致了這類游戲必然隨時都有大量的信息反饋和大量的命令等待輸入。策略類游戲的HUD如此的復(fù)雜,以至于在控制器鍵位相對較少的游戲主機(jī)上,這類游戲的流行程度始終達(dá)不到PC上同類游戲的流行度。游戲本身機(jī)制導(dǎo)致的復(fù)雜HUD設(shè)計應(yīng)該是這類游戲受眾群體局限性產(chǎn)生的原因之一。
其它類型的游戲,比如平臺游戲,益智游戲等等在HUD設(shè)計上感覺并沒有什么特別的地方。它們的HUD只要能清晰的交代出游戲人物(如果有的話)的狀態(tài)和游戲的進(jìn)展程度(得分之類的信息)就可以了。
如果看到這里覺得累了,依舊是欣賞一部分近期的3A游戲大作的UI界面美圖吧:
《孤島危機(jī)3》:
《仙劍奇?zhèn)b傳五 前傳》:
《刺客信條4黑旗》:
三、開始GUI系統(tǒng)的設(shè)計
在這篇文章里面,我們將一起去實(shí)現(xiàn)一個簡單而健全的GUI系統(tǒng)。這個系統(tǒng)既有HUD的功能,也可以創(chuàng)建GUI菜單。依舊是和之前的其他系統(tǒng)一樣,封裝在一個類中,這次的類是D3DGUIClass。
下面又到了天馬行空的設(shè)計時刻了——在實(shí)現(xiàn)GUI系統(tǒng)之前,讓我們設(shè)計出心目中的GUI系統(tǒng)該有的功能。
1.大體說明
首先需要說明的是,我們這次設(shè)計的GUI系統(tǒng)主要用于演示之用,擴(kuò)展性很強(qiáng),需要的更多功能完全可以在這個GUI的系統(tǒng)上進(jìn)行二次開發(fā),來增強(qiáng)它的特性和功能。
GUI系統(tǒng)主要由按鈕和靜態(tài)文本控件對象組成,當(dāng)然,還少不了背景圖的顯示。由于GUI系統(tǒng)是2D的,這意味著指定對象位置時,是用不到Z軸的。另外,我們決定使用正交投影的方式來渲染GUI,這樣可以根據(jù)像素的位置來指定屏幕的位置。
讓我們來回憶一下,(0,0)對應(yīng)的是屏幕的左上角。在windows系統(tǒng)中,左上角就是(0,0)。有了這個信息,就可以輕松地確定玩家的鼠標(biāo)指針是否懸停在GUI系統(tǒng)的控件之上,或者說正在點(diǎn)擊這個控件。
2.關(guān)于布局規(guī)劃和背景圖
在設(shè)計和規(guī)劃按鈕和文本位置時,我們只要牢記窗口左上角的坐標(biāo)是(0,0),就能隨心所欲的創(chuàng)建各式各樣的限制的系統(tǒng)范圍內(nèi)的心儀的GUI布局出來。對于背景圖的話,其實(shí)沒必要考慮它的位置,因?yàn)檫@個圖像就是一個由兩個三角形構(gòu)成的全屏圖像。只要系統(tǒng)知道當(dāng)前程序的寬度和高度,就可以顯示一個全屏紋理圖。
3.關(guān)于按鈕控件的實(shí)現(xiàn)
必不可少的特性就是可以點(diǎn)擊的按鈕控件,其實(shí)按鈕控件的實(shí)現(xiàn)比點(diǎn)擊圖像更容易實(shí)現(xiàn)。由于按鈕控件的位置是以像素來指定的,因此為了確定鼠標(biāo)指針是否在按鈕上或者按下了按鈕,其實(shí)就是檢查當(dāng)前鼠標(biāo)指針的位置是否落在按鈕區(qū)域內(nèi)。因?yàn)閃indows操作系統(tǒng)用像素指定鼠標(biāo)位置且屏幕的左上角為(0,0),所以檢查鼠標(biāo)的坐標(biāo)是否處于按鈕四個角的坐標(biāo)范圍內(nèi)就可以了,即檢查如下四個方面:
按鈕的左側(cè)坐標(biāo)位置是否小于鼠標(biāo)指針的X坐標(biāo),按鈕右側(cè)坐標(biāo)位置是否大于鼠標(biāo)指針的X坐標(biāo),按鈕的上側(cè)坐標(biāo)是否小于鼠標(biāo)指針的Y坐標(biāo),下側(cè)坐標(biāo)是否大于鼠標(biāo)的Y坐標(biāo)。如果四個都為真的話,那么就可以認(rèn)定鼠標(biāo)指針就在按鈕控件之上了,然后便通過消息過程來檢測是否按下的鼠標(biāo)左鍵這個狀態(tài),如果按下了鼠標(biāo)左鍵,那么就可以確定玩家不僅僅是把鼠標(biāo)放在了按鈕之上,而是同時點(diǎn)擊了按鈕。我們還在這個GUI系統(tǒng)中實(shí)現(xiàn)了通過鼠標(biāo)的懸停和點(diǎn)擊動作,來改變按鈕的外觀,在按下按鈕后有一定的動畫效果。
根據(jù)上面的描述,我們可以寫出的實(shí)現(xiàn)代碼如下:
其中pControl是一個自定義的GUICONTROL結(jié)構(gòu)體的指針對象
//檢查鼠標(biāo)是否懸?;蛘唿c(diǎn)擊了按鈕 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.類的框架設(shè)計
因?yàn)檫@個GUI系統(tǒng)的實(shí)現(xiàn)還有一定的細(xì)節(jié)需要詳細(xì)說明,所以決定分兩次更新來講解。這次我們先把功能完整的類的實(shí)現(xiàn)結(jié)果給大家,并告訴大家如何使用,說明一下main函數(shù)相對于之前有哪些細(xì)節(jié)需要改變,然后GUI系統(tǒng)類的實(shí)現(xiàn)細(xì)節(jié)留待下篇講解(如果需要額外用一次更新來講解的話)。
還是把詳細(xì)注釋的頭文件貼一下吧:
//==================================================== // Name: D3DGUIClass.h // Des:一個游戲GUI界面系統(tǒng)類的頭文件 // 2013年 11月17日 Create by 淺墨 //==================================================== #pragma once // 所支持的控件類型宏 #define UGP_GUI_STATICTEXT 1 #define UGP_GUI_BUTTON 2 #define UGP_GUI_Background 3 // 鼠標(biāo)按鍵狀態(tài)宏 #define UGP_BUTTON_UP 1 #define UGP_BUTTON_OVER 2 #define UGP_BUTTON_DOWN 3 // 設(shè)置一些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靈活頂點(diǎn)類型的結(jié)構(gòu)體 struct GUIVERTEX { floatx, y, z, rhw; unsignedlong color; floattu, tv; }; #define D3DFVF_GUI (D3DFVF_XYZRHW |D3DFVF_DIFFUSE | D3DFVF_TEX1) //控件屬性結(jié)構(gòu)體 struct GUICONTROL { //操作類型,ID和顏色 intm_type; //控件類型 intm_id; //控件ID unsignedlong m_color; //控件顏色 intm_listID; //如果是文字的話,這個變量就表示它使用的字體,否則就表示頂點(diǎn)緩存 floatm_xPos, m_yPos; //控件的起始位置 floatm_width, m_height; // 控件的寬度和高度 wchar_t*m_text; // 文字內(nèi)容 LPDIRECT3DTEXTURE9m_Background; // 控件背景的填充圖像 LPDIRECT3DTEXTURE9m_upTex, m_downTex, m_overTex; // 存放按鈕彈起,按下和鼠標(biāo)經(jīng)過時的3張紋理圖 }; class D3DGUIClass { private: LPDIRECT3DDEVICE9m_pd3dDevice; //D3D設(shè)備對象 LPD3DXFONT*m_pFonts; //D3D字體對象 GUICONTROL*m_pControls; //控件對象 LPDIRECT3DVERTEXBUFFER9*m_pVertexBuffer; //頂點(diǎn)緩存對象指針 GUICONTROLm_Background; //背景圖對象 LPDIRECT3DVERTEXBUFFER9m_BackgroundBuffer; //背景圖緩沖區(qū)對象 boolm_bIsBackgroundUsed; //一個標(biāo)識,用于標(biāo)識是否已經(jīng)用了背景 intm_nTotalFontNum; //字體數(shù)目計數(shù)器 intm_nTotalControlNum; //控件數(shù)目計數(shù)器 intm_nTotalBufferNum; //緩沖區(qū)數(shù)目計數(shù)器 intm_nWindowWidth; //窗口寬度 intm_nWindowHeight; //窗口高度 public: D3DGUIClass(LPDIRECT3DDEVICE9device, int w, int h); ~D3DGUIClass(){ ClearUp(); } LPDIRECT3DDEVICE9GetD3dDevice() { return m_pd3dDevice; } //返回D3D設(shè)備對象的函數(shù) GUICONTROL*GetBackground() { return &m_Background; } //返回背景的函數(shù) LPDIRECT3DVERTEXBUFFER9GetBackgroundBuffer() { return m_BackgroundBuffer; } //返回背景緩沖區(qū)對象的函數(shù) intGetTotalFontNum() { return m_nTotalFontNum; } //返回所有字體數(shù)目的函數(shù) intGetTotalControlNum() { return m_nTotalControlNum; } //返回所有控件數(shù)目的函數(shù) intGetTotalBufferNum() { return m_nTotalBufferNum; } //返回總的緩沖區(qū)數(shù)目的函數(shù) intGetWindowWidth() { return m_nWindowWidth; } //返回窗口寬度的函數(shù) intGetWindowHeight() { return m_nWindowHeight; } //返回窗口高度的函數(shù) boolIsBackgroundUsed() { return m_bIsBackgroundUsed; } //返回背景是否在使用的bool值的函數(shù) voidSetWindowSize(int w, int h) { m_nWindowWidth = w; m_nWindowHeight = h; } //設(shè)置窗口寬度和高度的函數(shù) LPD3DXFONTGetFont(int id) //返回字體ID函數(shù) { if(id< 0 || id >= m_nTotalFontNum) return NULL; returnm_pFonts[id]; } GUICONTROL*GetGUIControl(int id) //返回GUI控件ID函數(shù) { if(id< 0 || id >= m_nTotalControlNum) return NULL; return&m_pControls[id]; } LPDIRECT3DVERTEXBUFFER9GetVertexBuffer(int id) //返回頂點(diǎn)緩存ID函數(shù) { if(id< 0 || id >= m_nTotalBufferNum) return NULL; returnm_pVertexBuffer[id]; } boolCreateTextFont(wchar_t *fontName, int size, int *fontID); //字體創(chuàng)建函數(shù) boolAddBackground(wchar_t *fileName); //GUI背景添加函數(shù) boolAddStaticText(int id, wchar_t *text, float x, float y, unsigned long color, intfontID); //添加靜態(tài)文本函數(shù) boolAddButton(int id, float x, float y, wchar_t *up, wchar_t *over, wchar_t *down);//添加按鈕函數(shù) voidClearUp( ); //資源清理函數(shù) }; void ProcessGUI(D3DGUIClass *gui, boolLMBDown, int mouseX, int mouseY, void(*funcPtr)(intid, int state)); //回調(diào)函數(shù)
具體的實(shí)現(xiàn)細(xì)節(jié)如果現(xiàn)在講會有一定的篇幅,我們留待下次講解吧。
四、GUI系統(tǒng)類的使用
接下來,一起來研究研究如果要在我們之前的demo框架里使用GUI系統(tǒng)的話,需要添加哪些代碼。
第一步,依舊是添加一些必要的全局變量:
D3DGUIClass g_gui= NULL; //創(chuàng)建GUI類對象 int g_FontID = -1; // GUI中字體對象的ID bool g_LMBDown= false; // GUI中的鼠標(biāo)狀態(tài)信息,鼠標(biāo)左鍵是否按下的標(biāo)識 int g_MouseX= 0, g_MouseY = 0; //存儲鼠標(biāo)坐標(biāo)的兩個變量
可以看到,在這里我們定義了自己寫的GUI系統(tǒng)類D3DGUIClass的類對象,然后是鼠標(biāo)狀態(tài)信息相關(guān)的變量和一個字體對象。
第二步,在消息處理函數(shù)的switch中添加一些新的case:
//-----------------------------------【W(wǎng)ndProc( )函數(shù)】-------------------------------------- // 描述:窗口過程函數(shù)WndProc,對窗口消息進(jìn)行處理 //------------------------------------------------------------------------------------------------ LRESULT CALLBACK WndProc( HWND hwnd, UINTmessage, WPARAM wParam, LPARAM lParam ) //窗口過程函數(shù)WndProc { switch(message ) //switch語句開始 { caseWM_PAINT: // 客戶區(qū)重繪消息 Direct3D_Render(hwnd,0.0f); //調(diào)用Direct3D_Render函數(shù),進(jìn)行畫面的繪制 ValidateRect(hwnd,NULL); // 更新客戶區(qū)的顯示 break; //跳出該switch語句 caseWM_KEYDOWN: // 鍵盤按下消息 if(wParam == VK_ESCAPE) // ESC鍵 DestroyWindow(hwnd); // 銷毀窗口, 并發(fā)送一條WM_DESTROY消息 break; caseWM_DESTROY: //窗口銷毀消息 Direct3D_CleanUp(); //調(diào)用Direct3D_CleanUp函數(shù),清理COM接口對象 PostQuitMessage(0 ); //向系統(tǒng)表明有個線程有終止請求。用來響應(yīng)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條件都不符合,則執(zhí)行該default語句 returnDefWindowProc( hwnd, message, wParam, lParam ); //調(diào)用缺省的窗口過程來為應(yīng)用程序沒有處理的窗口消息提供缺省的處理。 } return0; //正常退出 }
其實(shí)這一步就是在之前消息的基礎(chǔ)上,添加了左鍵按下,左鍵彈起,和鼠標(biāo)移動的消息響應(yīng)。
第三步,在進(jìn)行渲染資源準(zhǔn)備的Object_Init( )函數(shù)中,添加載入GUI系統(tǒng)中的資源到內(nèi)存中的相關(guān)代碼:
//-----------------------------------【Object_Init()函數(shù)】-------------------------------------- // 描述:渲染資源初始化函數(shù),在此函數(shù)中進(jìn)行要被渲染的物體的資源的初始化 //-------------------------------------------------------------------------------------------------- HRESULT Objects_Init() { //創(chuàng)建字體 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); //設(shè)置紋理采樣參數(shù) 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系統(tǒng)相關(guān)代碼】------------------------------- //創(chuàng)建GUI系統(tǒng) 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; //添加靜態(tài)文本到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個按鈕,分別是開始游戲,載入進(jìn)度,選項(xiàng)和退出游戲,每個按鈕對應(yīng)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; }
代碼中都注釋的比較詳細(xì),就不多說明了哈。
第四步,添加一個GUI系統(tǒng)的回調(diào)函數(shù):
void GUICallback(int id, int state) { switch(id) { caseBUTTON_ID_1: //“開始游戲”按鈕的相關(guān)代碼實(shí)現(xiàn) break; caseBUTTON_ID_2: //“載入游戲”按鈕的相關(guān)代碼實(shí)現(xiàn) break; caseBUTTON_ID_3: //“選項(xiàng)”按鈕的相關(guān)代碼實(shí)現(xiàn) break; caseBUTTON_ID_4: //如果點(diǎn)擊了退出按鈕,就退出程序 if(state== UGP_BUTTON_DOWN) PostQuitMessage(0); break; } }
這里是一個switch—case語句總領(lǐng)的回調(diào)函數(shù)。
大家也許暫時會對這個函數(shù)的使用不太理解,其實(shí)它和我們自定義的ProcessGUI有著千絲萬縷的聯(lián)系,具體內(nèi)容我們在出GUI的第二篇文章的時候再詳細(xì)講解。這里我們只要知道它是和ProcessGUI搞基的就可以了。
舉個例子吧,對其中的BUTTON_ID_1按鈕,就是指的GUI界面中的“Start Game”按鈕,而點(diǎn)擊之后的余下響應(yīng)代碼(也就是消息響應(yīng)代碼)就寫在這個case之后,比如說點(diǎn)擊了新游戲的開始后需要渲染游戲畫面等等一系列代碼。
第五步,在渲染五步曲的第三步中調(diào)用一個封裝好功能的ProcessGUI函數(shù)就可以了。這個函數(shù)的具體實(shí)現(xiàn)我們在稍后退出的文章中會講到。
//-------------------------------------------------------------------------------------- //【Direct3D渲染五步曲之二】:開始繪制 //-------------------------------------------------------------------------------------- g_pd3dDevice->BeginScene(); // 開始繪制 //-------------------------------------------------------------------------------------- //【Direct3D渲染五步曲之三】:正式繪制 //-------------------------------------------------------------------------------------- //處理和渲染GUI系統(tǒng) ProcessGUI(g_gui,g_LMBDown, g_MouseX, g_MouseY, GUICallback); //-----------------------------【繪制文字信息】----------------------------- HelpText_Render(hwnd); //-------------------------------------------------------------------------------------- //【Direct3D渲染五步曲之四】:結(jié)束繪制 //-------------------------------------------------------------------------------------- g_pd3dDevice->EndScene(); // 結(jié)束繪制
五、詳細(xì)注釋的源代碼欣賞
這次的工程因?yàn)槭亲鳛镚UI的初步演示,自然就有了孑然一身的感覺,除了main.cpp和D3DUtil.h就是D3DGUIClass類的源文件和頭文件了。如下圖:
程序主要是實(shí)現(xiàn)了簡單的GUI系統(tǒng),通過GUI系統(tǒng)的功能在屏幕上添加了游戲菜單的四個按鈕,并且在屏幕上輸出了“淺墨DirectX教程第三季 之 打造游戲GUI界面”,"Version 淺墨1.0版"這兩段文字。
而且目前對“quit”退出按鈕的功能進(jìn)行了實(shí)行實(shí)現(xiàn),其實(shí)就簡單一句if(state == UGP_BUTTON_DOWN) PostQuitMessage(0);。。。。。
那么,老規(guī)矩,上程序的核心部分,main函數(shù)的代碼吧:
//-----------------------------------【程序說明】---------------------------------------------- // 【Visual C++】游戲開發(fā)系列配套源碼五十六 淺墨DirectX教程二十三 打造游戲GUI界面(一) // VS2010版 // 2013年11月 Create by 淺墨 // 背景音樂素材出處: 刺客信條 //------------------------------------------------------------------------------------------------ //-----------------------------------【宏定義部分】-------------------------------------------- // 描述:定義一些輔助宏 //------------------------------------------------------------------------------------------------ #define WINDOW_WIDTH 1366 //為窗口寬度定義的宏,以方便在此處修改窗口寬度 #define WINDOW_HEIGHT 768 //為窗口高度定義的宏,以方便在此處修改窗口高度 #define WINDOW_TITLE _T("【致我們永不熄滅的游戲開發(fā)夢想】 淺墨DirectX教程二十三 打造游戲GUI界面(一)博文配套示例程序 by淺墨") //為窗口標(biāo)題定義的宏 //-----------------------------------【頭文件包含部分】--------------------------------------- // 描述:包含程序所依賴的頭文件 //------------------------------------------------------------------------------------------------ #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") // 地板的頂點(diǎn)結(jié)構(gòu) 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設(shè)備對象 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; //一個浮點(diǎn)型的變量,代表幀速率 wchar_t g_strFPS[50] ={0}; //包含幀速率的字符數(shù)組 wchar_t g_strAdapterName[60] ={0}; //包含顯卡名稱的字符數(shù)組 D3DGUIClass *g_gui = NULL; //創(chuàng)建GUI類對象 bool g_LMBDown = false; // GUI中的鼠標(biāo)狀態(tài)信息,鼠標(biāo)左鍵是否按下的標(biāo)識 int g_MouseX = 0, g_MouseY = 0; //存儲鼠標(biāo)坐標(biāo)的兩個變量 int g_FontID = -1; // GUI中字體對象的ID //-----------------------------------【全局函數(shù)聲明部分】------------------------------------- // 描述:全局函數(shù)聲明,防止“未聲明的標(biāo)識”系列錯誤 //------------------------------------------------------------------------------------------------ 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); //-----------------------------------【W(wǎng)inMain( )函數(shù)】-------------------------------------- // 描述:Windows應(yīng)用程序的入口函數(shù),我們的程序從這里開始 //------------------------------------------------------------------------------------------------ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd) { //開始設(shè)計一個完整的窗口類 WNDCLASSEX wndClass={0} ; //用WINDCLASSEX定義了一個窗口類,即用wndClass實(shí)例化了WINDCLASSEX,用于之后窗口的各項(xiàng)初始化 wndClass.cbSize = sizeof( WNDCLASSEX ) ; //設(shè)置結(jié)構(gòu)體的字節(jié)數(shù)大小 wndClass.style = CS_HREDRAW | CS_VREDRAW; //設(shè)置窗口的樣式 wndClass.lpfnWndProc = WndProc; //設(shè)置指向窗口過程函數(shù)的指針 wndClass.cbClsExtra = 0; wndClass.cbWndExtra = 0; wndClass.hInstance = hInstance; //指定包含窗口過程的程序的實(shí)例句柄。 wndClass.hIcon=(HICON)::LoadImage(NULL,_T("GameMedia\\icon.ico"),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //從全局的::LoadImage函數(shù)從本地加載自定義ico圖標(biāo) wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); //指定窗口類的光標(biāo)句柄。 wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //為hbrBackground成員指定一個灰色畫刷句柄 wndClass.lpszMenuName = NULL; //用一個以空終止的字符串,指定菜單資源的名字。 wndClass.lpszClassName = _T("ForTheDreamOfGameDevelop"); //用一個以空終止的字符串,指定窗口類的名字。 if( !RegisterClassEx( &wndClass ) ) //設(shè)計完窗口后,需要對窗口類進(jìn)行注冊,這樣才能創(chuàng)建該類型的窗口 return -1; HWND hwnd = CreateWindow( _T("ForTheDreamOfGameDevelop"),WINDOW_TITLE, //喜聞樂見的創(chuàng)建窗口函數(shù)CreateWindow WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL ); //Direct3D資源的初始化,調(diào)用失敗用messagebox予以顯示 if (!(S_OK==Direct3D_Init (hwnd,hInstance))) { MessageBox(hwnd, _T("Direct3D初始化失敗~!"), _T("淺墨的消息窗口"), 0); //使用MessageBox函數(shù),創(chuàng)建一個消息窗口 } PlaySound(L"GameMedia\\Heart - 刺客信條.wav", NULL, SND_FILENAME | SND_ASYNC|SND_LOOP); //循環(huán)播放背景音樂 MoveWindow(hwnd,0,0,WINDOW_WIDTH,WINDOW_HEIGHT,true); //調(diào)整窗口顯示時的位置,窗口左上角位于屏幕坐標(biāo)(200,10)處 ShowWindow( hwnd, nShowCmd ); //調(diào)用Win32函數(shù)ShowWindow來顯示窗口 UpdateWindow(hwnd); //對窗口進(jìn)行更新,就像我們買了新房子要裝修一樣 //消息循環(huán)過程 MSG msg = { 0 }; //初始化msg while( msg.message != WM_QUIT ) //使用while循環(huán) { 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 ) ) //查看應(yīng)用程序消息隊(duì)列,有消息時將隊(duì)列中的消息派發(fā)出去。 { TranslateMessage( &msg ); //將虛擬鍵消息轉(zhuǎn)換為字符消息 DispatchMessage( &msg ); //該函數(shù)分發(fā)一個消息給窗口程序。 } else { Direct3D_Update(hwnd,fTimeDelta); //調(diào)用更新函數(shù),進(jìn)行畫面的更新 Direct3D_Render(hwnd,fTimeDelta); //調(diào)用渲染函數(shù),進(jìn)行畫面的渲染 } } UnregisterClass(_T("ForTheDreamOfGameDevelop"), wndClass.hInstance); return 0; } //-----------------------------------【W(wǎng)ndProc( )函數(shù)】-------------------------------------- // 描述:窗口過程函數(shù)WndProc,對窗口消息進(jìn)行處理 //------------------------------------------------------------------------------------------------ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) //窗口過程函數(shù)WndProc { switch( message ) //switch語句開始 { case WM_PAINT: // 客戶區(qū)重繪消息 Direct3D_Render(hwnd,0.0f); //調(diào)用Direct3D_Render函數(shù),進(jìn)行畫面的繪制 ValidateRect(hwnd, NULL); // 更新客戶區(qū)的顯示 break; //跳出該switch語句 case WM_KEYDOWN: // 鍵盤按下消息 if (wParam == VK_ESCAPE) // ESC鍵 DestroyWindow(hwnd); // 銷毀窗口, 并發(fā)送一條WM_DESTROY消息 break; case WM_DESTROY: //窗口銷毀消息 Direct3D_CleanUp(); //調(diào)用Direct3D_CleanUp函數(shù),清理COM接口對象 PostQuitMessage( 0 ); //向系統(tǒng)表明有個線程有終止請求。用來響應(yīng)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條件都不符合,則執(zhí)行該default語句 return DefWindowProc( hwnd, message, wParam, lParam ); //調(diào)用缺省的窗口過程來為應(yīng)用程序沒有處理的窗口消息提供缺省的處理。 } return 0; //正常退出 } //-----------------------------------【Direct3D_Init( )函數(shù)】---------------------------------- // 描述:Direct3D初始化函數(shù),進(jìn)行Direct3D的初始化 //------------------------------------------------------------------------------------------------ HRESULT Direct3D_Init(HWND hwnd,HINSTANCE hInstance) { //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之一,創(chuàng)接口】:創(chuàng)建Direct3D接口對象, 以便用該Direct3D對象創(chuàng)建Direct3D設(shè)備對象 //-------------------------------------------------------------------------------------- LPDIRECT3D9 pD3D = NULL; //Direct3D接口對象的創(chuàng)建 if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口對象,并進(jìn)行DirectX版本協(xié)商 return E_FAIL; //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之二,取信息】:獲取硬件設(shè)備信息 //-------------------------------------------------------------------------------------- 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; //支持硬件頂點(diǎn)運(yùn)算,我們就采用硬件頂點(diǎn)運(yùn)算,妥妥的 else vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件頂點(diǎn)運(yùn)算,無奈只好采用軟件頂點(diǎn)運(yùn)算 //-------------------------------------------------------------------------------------- // 【Direct3D初始化四步曲之三,填內(nèi)容】:填充D3DPRESENT_PARAMETERS結(jié)構(gòu)體 //-------------------------------------------------------------------------------------- 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初始化四步曲之四,創(chuàng)設(shè)備】:創(chuàng)建Direct3D設(shè)備接口 //-------------------------------------------------------------------------------------- if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd, vp, &d3dpp, &g_pd3dDevice))) return E_FAIL; //獲取顯卡信息到g_strAdapterName中,并在顯卡名稱之前加上“當(dāng)前顯卡型號:”字符串 wchar_t TempName[60]=L"當(dāng)前顯卡型號:"; //定義一個臨時字符串,且方便了把"當(dāng)前顯卡型號:"字符串引入我們的目的字符串中 D3DADAPTER_IDENTIFIER9 Adapter; //定義一個D3DADAPTER_IDENTIFIER9結(jié)構(gòu)體,用于存儲顯卡信息 pD3D->GetAdapterIdentifier(0,0,&Adapter);//調(diào)用GetAdapterIdentifier,獲取顯卡信息 int len = MultiByteToWideChar(CP_ACP,0, Adapter.Description, -1, NULL, 0);//顯卡名稱現(xiàn)在已經(jīng)在Adapter.Description中了,但是其為char類型,我們要將其轉(zhuǎn)為wchar_t類型 MultiByteToWideChar(CP_ACP, 0, Adapter.Description, -1, g_strAdapterName, len);//這步操作完成后,g_strAdapterName中就為當(dāng)前我們的顯卡類型名的wchar_t型字符串了 wcscat_s(TempName,g_strAdapterName);//把當(dāng)前我們的顯卡名加到“當(dāng)前顯卡型號:”字符串后面,結(jié)果存在TempName中 wcscpy_s(g_strAdapterName,TempName);//把TempName中的結(jié)果拷貝到全局變量g_strAdapterName中,大功告成~ if(!(S_OK==Objects_Init())) return E_FAIL; SAFE_RELEASE(pD3D) //LPDIRECT3D9接口對象的使命完成,我們將其釋放掉 return S_OK; } //-----------------------------------【Object_Init( )函數(shù)】-------------------------------------- // 描述:渲染資源初始化函數(shù),在此函數(shù)中進(jìn)行要被渲染的物體的資源的初始化 //-------------------------------------------------------------------------------------------------- HRESULT Objects_Init() { //創(chuàng)建字體 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); //設(shè)置紋理采樣參數(shù) 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系統(tǒng)相關(guān)代碼】------------------------------- // 創(chuàng)建GUI系統(tǒng) 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; // 添加靜態(tài)文本到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個按鈕,分別是開始游戲,載入進(jìn)度,選項(xiàng)和退出游戲,每個按鈕對應(yīng)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: //“開始游戲”按鈕的相關(guān)代碼實(shí)現(xiàn) break; case BUTTON_ID_2: //“載入游戲”按鈕的相關(guān)代碼實(shí)現(xiàn) break; case BUTTON_ID_3: //“選項(xiàng)”按鈕的相關(guān)代碼實(shí)現(xiàn) break; case BUTTON_ID_4: //如果點(diǎn)擊了退出按鈕,就退出程序 if(state == UGP_BUTTON_DOWN) PostQuitMessage(0); break; } } //-----------------------------------【Direct3D_Update( )函數(shù)】-------------------------------- // 描述:不是即時渲染代碼但是需要即時調(diào)用的,如按鍵后的坐標(biāo)的更改,都放在這里 //-------------------------------------------------------------------------------------------------- void Direct3D_Update( HWND hwnd,FLOAT fTimeDelta) { //GUI的實(shí)現(xiàn)暫時不需要在這里寫代碼 } //-----------------------------------【Direct3D_Render( )函數(shù)】------------------------------- // 描述:使用Direct3D進(jìn)行渲染 //-------------------------------------------------------------------------------------------------- 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系統(tǒng) ProcessGUI(g_gui, g_LMBDown, g_MouseX, g_MouseY, GUICallback); //-----------------------------【繪制文字信息】----------------------------- HelpText_Render(hwnd); //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之四】:結(jié)束繪制 //-------------------------------------------------------------------------------------- g_pd3dDevice->EndScene(); // 結(jié)束繪制 //-------------------------------------------------------------------------------------- // 【Direct3D渲染五步曲之五】:顯示翻轉(zhuǎn) //-------------------------------------------------------------------------------------- g_pd3dDevice->Present(NULL, NULL, NULL, NULL); // 翻轉(zhuǎn)與顯示 } //-----------------------------------【HelpText_Render( )函數(shù)】------------------------------- // 描述:封裝了幫助信息的函數(shù) //-------------------------------------------------------------------------------------------------- void HelpText_Render(HWND hwnd) { //定義一個矩形,用于獲取主窗口矩形 RECT formatRect; GetClientRect(hwnd, &formatRect); //在窗口右上角處,顯示每秒幀數(shù) 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( )函數(shù)】------------------------------------------ // 描述:用于計算每秒幀速率的一個函數(shù) //-------------------------------------------------------------------------------------------------- float Get_FPS() { //定義四個靜態(tài)變量 static float fps = 0; //我們需要計算的FPS值 static int frameCount = 0;//幀數(shù) static float currentTime =0.0f;//當(dāng)前時間 static float lastTime = 0.0f;//持續(xù)時間 frameCount++;//每調(diào)用一次Get_FPS()函數(shù),幀數(shù)自增1 currentTime = timeGetTime()*0.001f;//獲取系統(tǒng)時間,其中timeGetTime函數(shù)返回的是以毫秒為單位的系統(tǒng)時間,所以需要乘以0.001,得到單位為秒的時間 //如果當(dāng)前時間減去持續(xù)時間大于了1秒鐘,就進(jìn)行一次FPS的計算和持續(xù)時間的更新,并將幀數(shù)值清零 if(currentTime - lastTime > 1.0f) //將時間控制在1秒鐘 { fps = (float)frameCount /(currentTime - lastTime);//計算這1秒鐘的FPS值 lastTime = currentTime; //將當(dāng)前時間currentTime賦給持續(xù)時間lastTime,作為下一秒的基準(zhǔn)時間 frameCount = 0;//將本次幀數(shù)frameCount值清零 } return fps; } //-----------------------------------【Direct3D_CleanUp( )函數(shù)】-------------------------------- // 描述:對Direct3D的資源進(jìn)行清理,釋放COM接口對象 //--------------------------------------------------------------------------------------------------- void Direct3D_CleanUp() { //釋放COM接口對象 SAFE_RELEASE(g_pd3dDevice); SAFE_RELEASE(g_pTextFPS) SAFE_RELEASE(g_pd3dDevice) SAFE_DELETE(g_gui) }
那么,最后一起看看運(yùn)行截圖吧:
鼠標(biāo)懸停在options按鈕之上,可以發(fā)現(xiàn)按鈕“下陷”了
點(diǎn)擊quit按鈕,游戲程序便會退出:
嗯,本篇文章到這里就基本結(jié)束了,最后放出本篇文章配套示例程序的下載地址。
本篇文章的配套源代碼請點(diǎn)擊這里下載:
【淺墨DirectX提高班】配套源代碼之二十三下載 (CSDN下載頻道)
【淺墨DirectX提高班】配套源代碼之二十三下載 (百度云盤)
文章最后,依舊是送大家一些正能量,不過今天有所不同,它們是:
Seven Things I Would Really Tresure In Life(生命中我將珍視的七個習(xí)慣):
1) 對某件事(某種物)持久的熱愛和堅持。
2)早晨起來寫下今天要做的三件事,晚上入睡前能真的將它們做完。
3)永遠(yuǎn)對未知和未見的充滿著好奇,當(dāng)它們出現(xiàn)時毫不猶豫地去擁抱它們。
4)并不因他人的非議或外界的阻力而改變對自己的看法的堅持,當(dāng)要被迫使違背心愿去做某事時堅定不移地說"不"。
5)活在當(dāng)下,珍視已有的一切,someday you will miss today.
6)對和自己完全無關(guān)的人,僅因其所處的境遇而產(chǎn)生同情與關(guān)心,并盡己所能去幫助。
7)對一個更好的世界和一個更好的自己的向往與不放下的執(zhí)念。
是的,塑造自己的過程會很疼,但是最終你能收獲一個更好的自己。加油:)
下周一,讓我們離游戲開發(fā)的夢想更近一步。
下周一,游戲開發(fā)筆記,我們,不見不散。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。