本系列文章由zhmxy555(毛星云)编写,转载请注明出处。
文章链接: http://blog.csdn.net/poem_qianmo/article/details/16922703
作者:毛星云(浅墨) 邮箱: happylifemxy@163.com
hello,我们又如约相见了。:)上一讲中我们已经实现了一个简单的GUI系统,显示出了游戏的GUI主菜单页面,而本篇文章的主要目的是在之前GUI系统的基础上,实现GUI中多个页面间的切换和返回,更具有实用意义。
首先依然是放出截图吧,首先是主菜单页面:
【开始新游戏】界面:
【载入游戏】界面:
【选项】界面:
嗯,做出来的效果还是可圈可点的。
为了更好理解的这篇文章的内容,推荐大家先回去稍微瞄一下之前的那篇文章,这里贴心的给出传送门:
【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)
那么,下面我们就继续开始我们的革命吧,首先完成之前遗留下来的任务,讲一下我们GUI系统的心脏——ProcessGUI。
一、核心函数ProcessGUI的讲解
这个传说中的ProcessGUI函数可谓是我们GUI系统中的最重要的一个函数,它渲染了整个GUI系统,同样还为控件调用了回调函数。它参数包括需要处理的GUI,表明鼠标左键是否被按下的布尔值,然后是以像素为单位的鼠标指针位置,以及指向处理控件时要用到的通用回调函数的指针。另外我们知道,一般而言对于回调函数的话,需要两个参数,一个是控件的ID,另一个就是控件的状态。根据描述,函数原型就跃然纸上了:
void ProcessGUI(D3DGUIClass *gui, boolLMBDown, int mouseX, int mouseY, void(*funcPtr)(int id, int state)):
函数体写法方面的话,首先我们获取了D3D对象,渲染了背景图。因为顾名思义,他是背景图,就应该显示在各控件的最下方,所以我们需要在绘制其他控件之前绘制它,这样就可以在它之上随心所欲的绘制其他控件了。第一部分的代码写起来就是这样:
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来显示文本。那么代码写起来就是这样:
//用来显示文本的对象 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。根据我们的思路,代码就这样写:
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; }
接着就是根据不同的鼠标和按钮之间缠绵悱恻的状态来准备不同的纹理图绑定上去了:
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);