【Visual C++】游戏开发五十七 浅墨DirectX教程二十四 打造游戏GUI界面(二)

发表于2016-07-23
评论0 783浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594
  hello,我们又如约相见了。:)
  上一讲中我们已经实现了一个简单的GUI系统,显示出了游戏的GUI主菜单页面,而本篇文章的主要目的是在之前GUI系统的基础上,实现GUI中多个页面间的切换和返回,更具有实用意义。
  首先依然是放出截图吧,首先是主菜单页面:

 

  【开始新游戏】界面:
 

  【载入游戏】界面:
 

  【选项】界面:
 

  嗯,做出来的效果还是可圈可点的。
  为了更好理解的这篇文章的内容,推荐大家先回去稍微瞄一下之前的那篇文章,这里贴心的给出传送门:
  那么,下面我们就继续开始我们的革命吧,首先完成之前遗留下来的任务,讲一下我们GUI系统的心脏——ProcessGUI。

一、核心函数ProcessGUI的讲解
  这个传说中的ProcessGUI函数可谓是我们GUI系统中的最重要的一个函数,它渲染了整个GUI系统,同样还为控件调用了回调函数。它参数包括需要处理的GUI,表明鼠标左键是否被按下的布尔值,然后是以像素为单位的鼠标指针位置,以及指向处理控件时要用到的通用回调函数的指针。另外我们知道,一般而言对于回调函数的话,需要两个参数,一个是控件的ID,另一个就是控件的状态。根据描述,函数原型就跃然纸上了:

1
void ProcessGUI(D3DGUIClass *gui, boolLMBDown, int mouseX, int mouseY, void(*funcPtr)(int id, int state)):

  函数体写法方面的话,首先我们获取了D3D对象,渲染了背景图。因为顾名思义,他是背景图,就应该显示在各控件的最下方,所以我们需要在绘制其他控件之前绘制它,这样就可以在它之上随心所欲的绘制其他控件了。第一部分的代码写起来就是这样:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if(!gui)return
   
       LPDIRECT3DDEVICE9device = gui->GetD3dDevice(); 
       if(!device)return
   
       //绘制背景 
       GUICONTROL*Background = gui->GetBackground(); 
       LPDIRECT3DVERTEXBUFFER9bdBuffer = gui->GetBackgroundBuffer(); 
   
       //已经创建出的东西才绘制,所以来个if 
       if(gui->IsBackgroundUsed()&& Background && bdBuffer) 
       
                 device->SetTexture(0,Background->m_Background); 
                 device->SetStreamSource(0,bdBuffer, 0, sizeof(GUIVERTEX)); 
                 device->SetFVF(D3DFVF_GUI); 
                 device->DrawPrimitive(D3DPT_TRIANGLESTRIP,0, 2); 
                 device->SetTexture(0,NULL); 
       }

  因为我们GUI系统目前就只和二维空间打交道,那么在渲染GUI时,可以避免考虑深度测试这方面的问题。函数接下来要做的就是循环控件列表,并按照每个控件的类型对其加以渲染。在循环语句中有一个switch语句,它是用来检查正在处理的控件类型的。若是静态文本控件,就获取这个文本使用的D3D字体对象,设置一下文本位置,并且调用DrawText来显示文本。那么代码写起来就是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//用来显示文本的对象 
         LPD3DXFONTpFont = NULL; 
         RECTfontPosition = {0, 0, (long)gui->GetWindowWidth(), 
                   (long)gui->GetWindowHeight()}; 
    
         //创建一个顶点缓存对象用于按钮的渲染 
         LPDIRECT3DVERTEXBUFFER9pBuffer = NULL; 
         intstatus = UGP_BUTTON_UP; 
    
         //一个循环,用于各种控件的渲染 
         for(inti = 0; i < gui->GetTotalControlNum(); i++) 
         
                   //获取当前控件 
                   GUICONTROL*pControl = gui->GetGUIControl(i); 
                   if(!pControl)continue
    
                   //根据不同的类型做不同的操作 
                   switch(pControl->m_type) 
                   
                   caseUGP_GUI_STATICTEXT: 
                            //这种情况下获取字体对象 
                            pFont= gui->GetFont(pControl->m_listID); 
                            if(!pFont)continue
    
                            //设置文字位置 
                            fontPosition.left= pControl->m_xPos; 
                            fontPosition.top= pControl->m_yPos; 
    
                            //显示文字 
                            pFont->DrawText(NULL,pControl->m_text, -1, &fontPosition, 
                                     DT_LEFT,pControl->m_color); 
                            break;

  处理按钮的话,工作量就稍微大一点了。首先我们得到当前按钮使用的顶点缓存指针,这就和获取静态文本控件使用的D3D字体对象指针差不多。然后呢,启用透明混合处理功能,这样按钮可以有透明背景,可以为按钮增添些许效果,在设计按钮图像时就可以有更多的选择。接下来就顺势设置一下按钮状态。有了按钮状态的话,就可以确定要绑定的纹理了,我们使用if语句测试鼠标指针的位置是否落在按钮像素内,如果按钮的四个顶点的位置把鼠标指针的位置包含在其中了,那么就可以知道鼠标在按钮的区域里面。
  若鼠标指针落在按钮区域中并且鼠标左键被按下,我们就知道玩家正在单击按钮,就把按键状态设成UGP_BUTTON_DOWN。如果这都不满足的话,那么玩家就只是把鼠标指针放在了按钮之上,并没了点击按钮,就else设成UGP_BUTTON_OVER。根据我们的思路,代码就这样写:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
case UGP_GUI_BUTTON: 
                          status= UGP_BUTTON_UP; 
   
                          //获取按钮所对应的顶点缓存对象 
                          pBuffer= gui->GetVertexBuffer(pControl->m_listID); 
                          if(!pBuffer)continue
   
                          //设置纹理的alpha透明选项 
                          device->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE); 
                          device->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA); 
                          device->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA); 
   
                          //检查鼠标是否悬停或者点击了按钮 
                          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; 
                          }

  接着就是根据不同的鼠标和按钮之间缠绵悱恻的状态来准备不同的纹理图绑定上去了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(status== UGP_BUTTON_UP) device->SetTexture(0, pControl->m_upTex); 
if(status== UGP_BUTTON_OVER) device->SetTexture(0, pControl->m_overTex); 
if(status== UGP_BUTTON_DOWN) device->SetTexture(0, pControl->m_downTex);"font-family: 'Microsoft YaHei';">                         
 
  一切准备就绪,然后开始麻溜的进行渲染,关闭上alpha混合.。
 
//万事俱备,开始渲染按钮 
                        device->SetStreamSource(0,pBuffer, 0, sizeof(GUIVERTEX)); 
                        device->SetFVF(D3DFVF_GUI); 
                        device->DrawPrimitive(D3DPT_TRIANGLESTRIP,0, 2); 
   
                        //关闭alpha混合. 
                        device->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE); 
                        break
               }

  当然,最后我们不能忘了调用回调函数处理控件消息,也就是写上这一句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
    //调用回调函数处理控件消息 
    if(funcPtr) funcPtr(pControl->m_id, status); 
             
而为了给大家一个整体的概念,这里我们贴上这个函数的完整写法:
[cpp] view plain copy print?在CODE上查看代码片派生到我的代码片
//----------------------------------------------------------------------------- 
// Name:ProcessGUI 
// Desc: 全局的函数,封装渲染整个GUI系统,同样还为控件调用回调函数 
//----------------------------------------------------------------------------- 
void ProcessGUI(D3DGUIClass *gui, bool LMBDown, int mouseX, int mouseY, void(*funcPtr)(int id, int state)) 
    if(!gui) return
   
    LPDIRECT3DDEVICE9 device = gui->GetD3dDevice(); 
    if(!device) return
   
    // 绘制背景 
    GUICONTROL *Background = gui->GetBackground(); 
    LPDIRECT3DVERTEXBUFFER9 bdBuffer = gui->GetBackgroundBuffer(); 
   
    //已经创建出的东西才绘制,所以来个if 
    if(gui->IsBackgroundUsed() && Background && bdBuffer) 
    
        device->SetTexture(0, Background->m_Background); 
        device->SetStreamSource(0, bdBuffer, 0, sizeof(GUIVERTEX)); 
        device->SetFVF(D3DFVF_GUI); 
        device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); 
        device->SetTexture(0, NULL); 
    
   
    //用来显示文本的对象 
    LPD3DXFONT pFont = NULL; 
    RECT fontPosition = {0, 0, (long)gui->GetWindowWidth(), 
        (long)gui->GetWindowHeight()}; 
   
    // 创建一个顶点缓存对象用于按钮的渲染 
    LPDIRECT3DVERTEXBUFFER9 pBuffer = NULL; 
    int status = UGP_BUTTON_UP; 
   
    // 一个循环,用于各种控件的渲染 
    for(int i = 0; i < gui->GetTotalControlNum(); i++) 
    
        // 获取当前控件 
        GUICONTROL *pControl = gui->GetGUIControl(i); 
        if(!pControl) continue
   
        // 根据不同的类型做不同的操作 
        switch(pControl->m_type) 
        
        case UGP_GUI_STATICTEXT: 
            // 这种情况下获取字体对象 
            pFont = gui->GetFont(pControl->m_listID); 
            if(!pFont) continue
   
            // 设置文字位置 
            fontPosition.left = pControl->m_xPos; 
            fontPosition.top = pControl->m_yPos; 
   
            // 显示文字 
            pFont->DrawText(NULL, pControl->m_text, -1, &fontPosition, 
                DT_LEFT, pControl->m_color); 
            break
   
        case UGP_GUI_BUTTON: 
            status = UGP_BUTTON_UP; 
   
            //获取按钮所对应的顶点缓存对象 
            pBuffer = gui->GetVertexBuffer(pControl->m_listID); 
            if(!pBuffer) continue
   
            // 设置纹理的alpha透明选项 
            device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); 
            device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); 
            device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 
   
            //检查鼠标是否悬停或者点击了按钮 
            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; 
                else status = UGP_BUTTON_OVER; 
            
   
            //根据不同的鼠标和按钮之间缠绵悱恻的状态来准备不同的纹理图  
            if(status == UGP_BUTTON_UP) device->SetTexture(0, pControl->m_upTex); 
            if(status == UGP_BUTTON_OVER) device->SetTexture(0, pControl->m_overTex); 
            if(status == UGP_BUTTON_DOWN) device->SetTexture(0, pControl->m_downTex); 
   
            // 万事俱备,开始渲染按钮 
            device->SetStreamSource(0, pBuffer, 0, sizeof(GUIVERTEX)); 
            device->SetFVF(D3DFVF_GUI); 
            device->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); 
   
            // 关闭alpha混合. 
            device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); 
            break
        
   
        //调用回调函数处理控件消息 
        if(funcPtr) funcPtr(pControl->m_id, status); 
    
}

  这个看起来大块头的函数被我们这样一肢解,其实就很简单了。
  接着,就来看看如何一步一步实现上面的多页面间的切换效果吧。

二、实现GUI系统的多页面间切换
  上一节中我们自己设计和实现出了一个GUI系统的类D3DGUIClass,然后在实际应用中定义了一个类对象,就可以很轻松的实现游戏主窗口页面的显示。
  那么,如果想实现GUI系统的多页面间切换,一种比较容易想到的做法就是,定义多个D3DGUIClass类对象,来分别代表多个GUI页面。
  现在就开始把想法付诸实践吧。依然是一个五步曲:

  第一步,依旧是老规矩,很多宏的声明,在D3DGUIClass.h头文件中:
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 菜单页面的宏定义 
#define GUI_MAIN_SCREEN       1 
#define GUI_START_SCREEN      2 
#define GUI_LOAD_SCREEN       3 
#define GUI_OPTION_SCREEN    4 
    
// 设置一些GUI中用到的控件ID 
#define STATIC_TEXT_ID     1 
#define BUTTON_START_ID    2 
#define BUTTON_LOAD_ID     3 
#define BUTTON_OPTION_ID   4 
#define BUTTON_QUIT_ID     5 
#define BUTTON_BACK_ID     6 
#define BUTTON_LEVEL_1_ID  7

  第二步,顺势在main.cpp中添加一些全局变量:

1
2
3
4
5
6
7
8
9
10
11
//创建四个GUI类对象,分别代表四个页面 
D3DGUIClass            *g_MainGUI= NULL;//主窗口 
D3DGUIClass            *g_StartGUI= NULL; //游戏开始窗口 
D3DGUIClass            *g_LoadGUI= NULL; //游戏载入窗口 
D3DGUIClass            *g_OptionGUI= NULL; //游戏设置窗口 
    
int                    g_MainGUIFontID= -1;                  //  GUI中字体对象的ID 
int                    g_StartGUIFontID= -1;                 //  GUI中字体对象的ID 
int                    g_LoadGUIFontID= -1;                  //  GUI中字体对象的ID 
int                    g_OptionGUIFontID= -1;                //  GUI中字体对象的ID 
int                    g_currentGUI= GUI_MAIN_SCREEN;    //一个当前的GUI标识

  第三步,在进行渲染资源准备的Object_Init( )函数中,添加载入GUI系统中的资源到内存中的相关代码。这里的代码最好分为几个部分来写,便会显得思路清晰。
  首先,创建出这些GUI系统的页面,然后给四个页面分别添加背景图,接着分别给四个页面添加字体
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//--------------------------------------------------【GUI系统相关代码】------------------------------------------------------- 
    
       // 创建一些GUI系统 
       g_MainGUI = new D3DGUIClass(g_pd3dDevice,WINDOW_WIDTH, WINDOW_HEIGHT); //主菜单页面 
       g_StartGUI = newD3DGUIClass(g_pd3dDevice, WINDOW_WIDTH, WINDOW_HEIGHT); //开始新游戏页面 
       g_LoadGUI = new D3DGUIClass(g_pd3dDevice,WINDOW_WIDTH, WINDOW_HEIGHT); //载入游戏页面 
       g_OptionGUI = new D3DGUIClass(g_pd3dDevice,WINDOW_WIDTH, WINDOW_HEIGHT); //设置页面 
    
       // 给四个页面分别添加背景图 
       if(!g_MainGUI->AddBackground(L"GameMedia/maingui.jpg"))return false;  
       if(!g_StartGUI->AddBackground(L"GameMedia/startgui.jpg"))return false
       if(!g_LoadGUI->AddBackground(L"GameMedia/loadgui.jpg"))return false
       if(!g_OptionGUI->AddBackground(L"GameMedia/optiongui.jpg"))return false
    
    
       // 分别给四个页面添加字体 
       if(!g_MainGUI->CreateTextFont(L"微软雅黑", 28, &g_MainGUIFontID))return false
       if(!g_StartGUI->CreateTextFont(L"微软雅黑", 38, &g_StartGUIFontID))return false
       if(!g_LoadGUI->CreateTextFont(L"微软雅黑", 38, &g_LoadGUIFontID))return false
       if(!g_OptionGUI->CreateTextFont(L"微软雅黑", 38, &g_OptionGUIFontID))return false

  然后,是给主菜单页面添加一些布局的代码:
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//---------------------------------【主菜单main页面相关的页面布局代码】--------------------------------- 
       //添加静态文本到页面中 
       if(!g_MainGUI->AddStaticText(STATIC_TEXT_ID,L"Version 浅墨2.0版"
                 1170,735, D3DCOLOR_XRGB(55,155,255), g_MainGUIFontID)) return false
   
       if(!g_MainGUI->AddStaticText(STATIC_TEXT_ID,L"浅墨DirectX教程第三季 之 打造游戏GUI界面"
                 500,150, D3DCOLOR_XRGB(255,255,255), g_MainGUIFontID)) return false
   
   
       //添加4个按钮,分别是开始游戏,载入进度,选项和退出游戏,每个按钮对应3幅图 
       if(!g_MainGUI->AddButton(BUTTON_START_ID,650, 340, L"GameMedia\startUp.png"
                 L"GameMedia\StartOver.png",L"GameMedia\startDown.png")) return false
   
       if(!g_MainGUI->AddButton(BUTTON_LOAD_ID,650, 385, L"GameMedia\loadUp.png"
                 L"GameMedia\loadOver.png",L"GameMedia\loadDown.png")) return false
   
       if(!g_MainGUI->AddButton(BUTTON_OPTION_ID,650, 430, L"GameMedia\optionsUp.png"
                 L"GameMedia\optionsOver.png",L"GameMedia\optionsDown.png")) return false
   
       if(!g_MainGUI->AddButton(BUTTON_QUIT_ID,650, 475, L"GameMedia\quitUp.png"
                 L"GameMedia\quitOver.png",L"GameMedia\quitDown.png")) return false

  接着,是给开始新游戏页面添加一些布局的代码:

1
2
3
4
5
6
7
//------------------------【开始新游戏start页面相关的页面布局代码】------------------------ 
        //添加按钮到页面中 
        if(!g_StartGUI->AddButton(BUTTON_LEVEL_1_ID,550,380, L"GameMedia/level1Up.png",L"GameMedia/level1Over.png"
                  L"GameMedia/level1Down.png"))return false
   
        if(!g_StartGUI->AddButton(BUTTON_BACK_ID,750, 350, L"GameMedia/backUp.png",L"GameMedia/backOver.png"
                  L"GameMedia/backDown.png"))return false;

  这里代码就比较简洁了,所以页面中自然就会显得空荡荡的,一千个人眼中有一千个哈姆雷特,大家可以根据自己的意愿,想在页面中添加什么控件就怎么去加代码。
  又接着,是给载入游戏页面添加一些布局的代码:
       
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//------------------------【载入游戏load页面相关的页面布局代码】------------------------ 
       //添加静态文本到页面中 
       if(!g_LoadGUI->AddStaticText(STATIC_TEXT_ID,L"这里是load game页面"
                 411,340, D3DCOLOR_XRGB(33,255,55), g_LoadGUIFontID)) return false
       //添加按钮到页面中 
       if(!g_LoadGUI->AddButton(BUTTON_BACK_ID,750, 400, L"GameMedia/backUp.png",L"GameMedia/backOver.png"
                 L"GameMedia/backDown.png"))return false
 
  依然是很简洁的代码,想额外添加什么就自己做主吧~
  最后是给option页面加上一些布局的代码:
  
//------------------------【游戏设置option页面相关的页面布局代码】------------------------ 
       //添加按钮到页面中 
       if(!g_OptionGUI->AddButton(BUTTON_BACK_ID,750, 450, L"GameMedia/backUp.png",L"GameMedia/backOver.png"
                 L"GameMedia/backDown.png"))return false
       //添加静态文本到页面中 
        if(!g_OptionGUI->AddStaticText(STATIC_TEXT_ID,L"这里是Option页面"
                         540,60, D3DCOLOR_XRGB(33,55,255), g_OptionGUIFontID)) return false

  我们可以发现上述代码基本上都是差不多的。其实,对如上四个不同页面布局的设置的话,就是看在g_MainGUI ,g_LoadGUI ,g_OptionGUI,g_StartGUI四个参数中选哪个来AddStaticText或者AddButton的。
  第四步,修改我们的GUI系统的回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//-----------------------------------【GUICallback( )函数】--------------------------------------- 
//      描述:GUI系统的回调函数,填写按钮按下后的相关处理代码 
//-------------------------------------------------------------------------------------------------- 
void GUICallback(int id, int state) 
         switch(id) 
         
         caseBUTTON_START_ID:   //start game开始游戏按钮 
                   if(state== UGP_BUTTON_DOWN) 
                            g_currentGUI= GUI_START_SCREEN; 
                   break
    
         caseBUTTON_LOAD_ID:  //load game载入游戏按钮 
                   if(state== UGP_BUTTON_DOWN) 
                            g_currentGUI= GUI_LOAD_SCREEN; 
                   break
    
         caseBUTTON_OPTION_ID: //option设置按钮 
                   if(state== UGP_BUTTON_DOWN) 
                            g_currentGUI= GUI_OPTION_SCREEN; 
                   break
    
         caseBUTTON_BACK_ID: //back返回按钮 
                   if(state== UGP_BUTTON_DOWN) 
                            g_currentGUI= GUI_MAIN_SCREEN; 
                   break
    
         caseBUTTON_QUIT_ID://quit退出按钮 
                   if(state== UGP_BUTTON_DOWN) 
                            PostQuitMessage(0); 
                   break
    
         caseBUTTON_LEVEL_1_ID:  //start game开始游戏页面中,Level1按钮 
                   //等级一的游戏从这里开始写代码 
                   break
         
}

  相较于之前的那个GUI回调函数,我们添加了一些按钮。然后因为在步骤一中控件的ID有变,所以这里的case后面的控件ID自然有改变。
 
  第五步,在渲染五步曲的第三步中,用一个一个if 、else if、else组合句,处理和渲染GUI系统,表示出几个页面之前的逻辑关系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//-----------------------------------【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渲染五步曲之三】:正式绘制 
         //-------------------------------------------------------------------------------------- 
    
    
         //一个if 、else if、else组合句,处理和渲染GUI系统 
         if(g_currentGUI== GUI_MAIN_SCREEN) 
                   ProcessGUI(g_MainGUI,g_LMBDown, g_MouseX, 
                   g_MouseY,GUICallback); 
         elseif(g_currentGUI == GUI_START_SCREEN) 
                   ProcessGUI(g_StartGUI,g_LMBDown, g_MouseX, 
                   g_MouseY,GUICallback); 
         elseif(g_currentGUI == GUI_LOAD_SCREEN) 
                   ProcessGUI(g_LoadGUI,g_LMBDown, g_MouseX, 
                   g_MouseY,GUICallback); 
         elseif(g_currentGUI == GUI_OPTION_SCREEN) 
                   ProcessGUI(g_OptionGUI,g_LMBDown, g_MouseX, 
                   g_MouseY,GUICallback); 
         else 
                   ProcessGUI(g_MainGUI,g_LMBDown, g_MouseX, 
                   g_MouseY,GUICallback); 
    
         //-----------------------------【绘制文字信息】----------------------------- 
         HelpText_Render(hwnd); 
    
    
         //-------------------------------------------------------------------------------------- 
         //【Direct3D渲染五步曲之四】:结束绘制 
         //-------------------------------------------------------------------------------------- 
         g_pd3dDevice->EndScene();                       // 结束绘制 
         //-------------------------------------------------------------------------------------- 
         //【Direct3D渲染五步曲之五】:显示翻转 
         //-------------------------------------------------------------------------------------- 
         g_pd3dDevice->Present(NULL,NULL, NULL, NULL);  // 翻转与显示 
    

  嗯,上面讲的内容就是这篇文章配套示例程序的实现思路了。接着来看一些新鲜的内容吧。

三、进阶GUI书籍推荐
  GUI相关的知识暂时就打算讲这两章了。如果大家嫌没过够瘾,那么浅墨给大家推荐一本专门讲解GUI的书籍。这本书的名字叫《Directx.9.User.Interfaces.-.Design.and.Implementation》,是一本专门介绍DirectX9中 UI设计和实现的一本书籍,虽然是英文原版,但是讲解清晰明了,很好理解。
  书封面是这样的:


  有对GUI有进阶了解兴趣的朋友们,不妨下载了回去研究研究。
  这里是下载地址:
  【浅墨推荐】Directx.9.User.Interfaces.-.Design.and.Implementation(百度云盘)
 
四、兼容DirectX的优秀开源GUI库推荐
  另外给大家推荐一个开源的GUI库—— CEGUI。CEGUI它为Crazy Eddie's GUI的缩写,它是一个用C++开发的面向对象的免费界面库,CEGUI是一个兼容OpenGL、DirectX的优秀开源GUI库,。针对游戏开发者,提供了3D环境中的窗口及其部件的图形API。可以在DirectX3D中使用CEGUI中使用。
  CRGUI的官方网站是: http://cegui.org.uk/   
  然后放两张DirectX中使用CEGUI的截图吧:
 

 
  比较炫的吧~所以呢,更多炫酷的游戏GUI推荐大家站在巨人的肩膀上实现。这样省时省力。

五、详细注释的源代码欣赏
  这次的工程除了main.cpp和D3DUtil.h就是D3DGUIClass类的源文件和头文件,非常简约。如下图:

 

  程序主要是实现了一个多页面的GUI系统,有主菜单页面,开始游戏页面,载入游戏页面,选项页面这四页面。
  后面的3个页面有返回按钮可以供我们返回到主菜单。然后“quit”退出按钮可以退出游戏程序。
  那么,老规矩,上程序的主体部分,main.cpp的代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
//-----------------------------------【程序说明】---------------------------------------------- 
// 【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  
#include  
#include  
#include   
#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};   //包含显卡名称的字符数组 
   
bool                                                g_LMBDown = false;      // GUI中的鼠标状态信息,鼠标左键是否按下的标识 
int                                             g_MouseX = 0, g_MouseY = 0;      //存储鼠标坐标的两个变量 
   
 //创建四个GUI类对象,分别代表四个页面 
D3DGUIClass     *g_MainGUI = NULL;//主窗口 
D3DGUIClass     *g_StartGUI = NULL; //游戏开始窗口 
D3DGUIClass     *g_LoadGUI = NULL; //游戏载入窗口 
D3DGUIClass     *g_OptionGUI = NULL; //游戏设置窗口 
   
int                     g_MainGUIFontID = -1;                       //  GUI中字体对象的ID 
int                     g_StartGUIFontID = -1;                      //  GUI中字体对象的ID 
int                     g_LoadGUIFontID = -1;                       //  GUI中字体对象的ID 
int                     g_OptionGUIFontID = -1;                 //  GUI中字体对象的ID 
int                     g_currentGUI = GUI_MAIN_SCREEN;    //一个当前的GUI标识 
   
   
//-----------------------------------【全局函数声明部分】------------------------------------- 
//  描述:全局函数声明,防止“未声明的标识”系列错误 
//------------------------------------------------------------------------------------------------ 
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\Assassins Creed Theme 刺客信条.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_MainGUI = new D3DGUIClass(g_pd3dDevice, WINDOW_WIDTH, WINDOW_HEIGHT); //主菜单页面 
    g_StartGUI = new D3DGUIClass(g_pd3dDevice, WINDOW_WIDTH, WINDOW_HEIGHT); //开始新游戏页面 
    g_LoadGUI = new D3DGUIClass(g_pd3dDevice, WINDOW_WIDTH, WINDOW_HEIGHT); //载入游戏页面 
    g_OptionGUI = new D3DGUIClass(g_pd3dDevice, WINDOW_WIDTH, WINDOW_HEIGHT); //设置页面 
   
    // 给四个页面分别添加背景图 
    if(!g_MainGUI->AddBackground(L"GameMedia/maingui.jpg")) return false;   
    if(!g_StartGUI->AddBackground(L"GameMedia/startgui.jpg")) return false
    if(!g_LoadGUI->AddBackground(L"GameMedia/loadgui.jpg")) return false
    if(!g_OptionGUI->AddBackground(L"GameMedia/optiongui.jpg")) return false
   
   
    // 分别给四个页面添加字体 
    if(!g_MainGUI->CreateTextFont(L"微软雅黑", 28, &g_MainGUIFontID)) return false
    if(!g_StartGUI->CreateTextFont(L"微软雅黑", 38, &g_StartGUIFontID)) return false
    if(!g_LoadGUI->CreateTextFont(L"微软雅黑", 38, &g_LoadGUIFontID)) return false
    if(!g_OptionGUI->CreateTextFont(L"微软雅黑", 38, &g_OptionGUIFontID)) return false
   
   
   
   
    //---------------------------------【主菜单main页面相关的页面布局代码】--------------------------------- 
    // 添加静态文本到页面中 
    if(!g_MainGUI->AddStaticText(STATIC_TEXT_ID, L"Version 浅墨2.0版"
        1170, 735, D3DCOLOR_XRGB(55,155,255), g_MainGUIFontID)) return false
   
    if(!g_MainGUI->AddStaticText(STATIC_TEXT_ID, L"浅墨DirectX教程第三季 之 打造游戏GUI界面"
        500, 150, D3DCOLOR_XRGB(255,255,255), g_MainGUIFontID)) return false
   
   
    // 添加4个按钮,分别是开始游戏,载入进度,选项和退出游戏,每个按钮对应3幅图 
    if(!g_MainGUI->AddButton(BUTTON_START_ID, 650, 340, L"GameMedia\startUp.png"
        L"GameMedia\StartOver.png", L"GameMedia\startDown.png")) return false
   
    if(!g_MainGUI->AddButton(BUTTON_LOAD_ID, 650, 385, L"GameMedia\loadUp.png"
        L"GameMedia\loadOver.png", L"GameMedia\loadDown.png")) return false
   
    if(!g_MainGUI->AddButton(BUTTON_OPTION_ID, 650, 430, L"GameMedia\optionsUp.png"
        L"GameMedia\optionsOver.png", L"GameMedia\optionsDown.png")) return false
   
    if(!g_MainGUI->AddButton(BUTTON_QUIT_ID, 650, 475, L"GameMedia\quitUp.png"
        L"GameMedia\quitOver.png", L"GameMedia\quitDown.png")) return false
   
   
    //------------------------【开始新游戏start页面相关的页面布局代码】------------------------ 
    // 添加按钮到页面中 
    if(!g_StartGUI->AddButton(BUTTON_LEVEL_1_ID,550, 380,  L"GameMedia/level1Up.png", L"GameMedia/level1Over.png"
        L"GameMedia/level1Down.png")) return false
   
    if(!g_StartGUI->AddButton(BUTTON_BACK_ID, 750, 350, L"GameMedia/backUp.png", L"GameMedia/backOver.png"
        L"GameMedia/backDown.png")) return false
   
   
    //------------------------【载入游戏load页面相关的页面布局代码】------------------------ 
    //添加静态文本到页面中 
    if(!g_LoadGUI->AddStaticText(STATIC_TEXT_ID, L"这里是load game页面"
        411, 340, D3DCOLOR_XRGB(33,255,55), g_LoadGUIFontID)) return false
    // 添加按钮到页面中 
    if(!g_LoadGUI->AddButton(BUTTON_BACK_ID, 750, 400, L"GameMedia/backUp.png", L"GameMedia/backOver.png"
        L"GameMedia/backDown.png")) return false
   
   
    //------------------------【游戏设置option页面相关的页面布局代码】------------------------ 
    // 添加按钮到页面中 
    if(!g_OptionGUI->AddButton(BUTTON_BACK_ID, 750, 450, L"GameMedia/backUp.png", L"GameMedia/backOver.png"
        L"GameMedia/backDown.png")) return false
    //添加静态文本到页面中 
     if(!g_OptionGUI->AddStaticText(STATIC_TEXT_ID, L"这里是Option页面"
            540, 60, D3DCOLOR_XRGB(33,55,255), g_OptionGUIFontID)) return false
   
   
   
    return S_OK; 
   
//-----------------------------------【GUICallback( )函数】--------------------------------------- 
//  描述:GUI系统的回调函数,填写按钮按下后的相关处理代码 
//-------------------------------------------------------------------------------------------------- 
void GUICallback(int id, int state) 
    switch(id) 
    
    case BUTTON_START_ID:   //start game开始游戏按钮 
        if(state == UGP_BUTTON_DOWN) 
            g_currentGUI = GUI_START_SCREEN; 
        break
   
    case BUTTON_LOAD_ID:  //load game载入游戏按钮 
        if(state == UGP_BUTTON_DOWN) 
            g_currentGUI = GUI_LOAD_SCREEN; 
        break
   
    case BUTTON_OPTION_ID: //option设置按钮 
        if(state == UGP_BUTTON_DOWN) 
            g_currentGUI = GUI_OPTION_SCREEN; 
        break
   
    case BUTTON_BACK_ID: //back返回按钮 
        if(state == UGP_BUTTON_DOWN) 
            g_currentGUI = GUI_MAIN_SCREEN; 
        break
   
    case BUTTON_QUIT_ID://quit退出按钮 
        if(state == UGP_BUTTON_DOWN) 
            PostQuitMessage(0); 
        break
   
    case BUTTON_LEVEL_1_ID:  //start game开始游戏页面中,Level1按钮 
        //等级一的游戏从这里开始写代码 
        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渲染五步曲之三】:正式绘制 
    //-------------------------------------------------------------------------------------- 
   
   
    // 一个if 、else if、else组合句,处理和渲染GUI系统 
    if(g_currentGUI == GUI_MAIN_SCREEN) 
        ProcessGUI(g_MainGUI, g_LMBDown, g_MouseX, 
        g_MouseY, GUICallback); 
    else if(g_currentGUI == GUI_START_SCREEN) 
        ProcessGUI(g_StartGUI, g_LMBDown, g_MouseX, 
        g_MouseY, GUICallback); 
    else if(g_currentGUI == GUI_LOAD_SCREEN) 
        ProcessGUI(g_LoadGUI, g_LMBDown, g_MouseX, 
        g_MouseY, GUICallback); 
    else if(g_currentGUI == GUI_OPTION_SCREEN) 
        ProcessGUI(g_OptionGUI, g_LMBDown, g_MouseX, 
        g_MouseY, GUICallback); 
    else 
        ProcessGUI(g_MainGUI, 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_MainGUI) 
    SAFE_DELETE(g_StartGUI) 
    SAFE_DELETE(g_LoadGUI) 
    SAFE_DELETE(g_OptionGUI) 
time.h>

  那么,最后一起看看运行截图吧。
  主页面,鼠标悬停在load game上,load game会变色:


  鼠标悬停或者点击开始页面中的 level1 他会有简单的变色动画:


  鼠标悬停在load game页面的back按钮上的效果:


  option页面:


  嗯,本篇文章到这里就基本结束了
  文章最后,依旧是送大家一些正能量,节选自《不要向这个世界认输,因为你还有牛逼的梦想》:
  有的时候当你走得太远,你会忘记你为什么走在这条路上,你会开始变得执着于眼前的东西,焦点于眼前的困难,急躁于眼前的利益,于是你开始一而再再而三地降低你自己的底线。你口口声声说要执行你自己的计划,可因为睡过了头就把计划一改再改。
  你总有千万种借口来降低你的底线,偏偏那些借口很面善,于是你渐渐地模糊了自己想要的,变成了一个自己不认识的自己。
  只是,你知道,生活它从来不会因为你放弃理想而简单一点点。
  也许我的人生剧本已经写好,就是这样平凡地过一辈子,我也要给这个剧本带来一点波澜起伏,我现在手头除了所剩不多的青春以外什么都没有,但是我知道我手头的这件东西,却能决定我变成一个什么样的人。
  没错,这个世界它总是告诉你梦想是很难实现的,实现的永远是少数人,它告诉你爱情是很难存在的,浪漫爱情故事里的主角永远不是你。它告诉你相处是一件很难的事情有的时候你很努力了也做不好。它告诉你很多时候你喜欢的人不会喜欢你,往往他们甚至不在乎你。这个XX的世界告诉你,不是所有付出都是有结果的。
  只是,如果我们因为一点点的争议就感觉不安,如果我们因为一点点的挫折就开始动摇,那只能说明我们只是这样的一个弱爆的人而已。
  所以,在认输之前,请绝不放过任何一个挑战世界的机会。
  晚安全世界 :)

原文链接

著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台