如何实现一个UI系统
如何为我的游戏实现一个UI系统,这个问题我想了很久,不过我现在可不像开始的时候那样一点思路也没有。如果你也被这个问题所困扰,我十分乐意与你分享这几天来的学习成果。嘿嘿,我是不是有点得意忘形了?
在开始之前,我要提醒你,学而不思则惘。在看这篇文章的时候,请时刻保持头脑清醒,如果有什么不太明白的话,请停下一两分钟,好好想想,这篇文章可不是囫囵吞枣就能看懂的哦!此外这篇文章是建立在部分实例和猜测的基础上的,可能存在着大量的不科学的想法和严重的错误,如果你在实践的过程中出现了问题欢迎提问,如果你发现了其中的错误请你指出来,如果将来你发现被误导了(当然我会尽力减小这种可能),请不要埋怨,因为是否继续往下看是你自己的决定。
1、窗口
UI系统的表现形式是什么?在开始前我们有必要弄清这个问题。
我们需要对话框、按钮、单选按钮、复选按钮、滚动条、下拉列表……好了好了,想不到你一口气竟能说出这么多种窗口。是的,这些都是不同形式的窗口,UI系统正是靠着形形色色的窗口展示自己,请记住这一点。如果你还是不明白,就看一看MSDN中的Hierarchy Chart。
2、理解windows的UI系统
windows这样一套经典的图形操作系统,我如果不拿它做例子,实在是有点儿对不住比尔大叔啊。
窗口都是矩形的,不要跟我说不规则窗口,其实那也是一个矩形的,只不过有些地方没有画而已。既然是矩形,只要知道它的长和宽(Width,Height,有点儿不一样是吧?windows里叫宽和高),它就确定了。然后,你把它放在某个位置上,所以它又有了坐标(xPos,yPos)。你的UI系统至少也要有这些数据,不然就没法画了。
然后是各式各样的事件,当鼠标经过的时候,按钮变亮了;当你按下Alt+F的时候,弹出了一个菜单。不管在windows里是哪个设备驱动把这些信息告诉了UI系统,我们的确需要它,不是吗?
一个菜单被按下然后一个对话框弹了出来,是谁的结果?是鼠标吗?怎么可能!当然是UI系统干的。UI系统不仅要接受各种输入设备的信息改变相应的外观,它还要根据不同的操作产生消息。菜单按下时,windows会主动地往消息队列里发送一个WM_COMMAND消息。至于对话框是否会弹出来,就要看你是否对这个消息进行处理了。
还有别的什么吗?
当然有啦,只是我也不很清楚,毕竟才研究了三四天嘛。不过我可以告诉你是什么:那么多不同类型的窗口,windows是怎么区分的?这个问题很关键啦,窗体如何绘制,事件如何激发全都需要判断的。比如说一个普通按钮和一个单选按钮,一个按下之后要弹起来,另一个要有一个圆点,明显的不同。既然都是窗口,windows是怎么做到的呢?我在winuser.h里面看到了大量关于STYLE的宏,我猜是用了CASE判断,可是为每一个窗口都作判断也太麻烦了吧,还在研究中。。。
3、窗口类的封装
封装是为了更好的使用,但不是必需的。就像使用MFC我们可以方便开发项目。(不要跟我讨论你对MFC的感情,那是你自己的事)然而如果没有他,只用SDK我们一样可以完成上司的任务。但是为了方便我建议你这样做,封装你的窗口类。我没有对此作太多的研究,我想把更多的时间放到研究UI系统本身上。如果你想了解得更多,请看这里http://blog.csdn.net/mythma
4、消息映射及命令绕行
和封装窗口类一样,这也只是一个辅助性的工作。这也是MFC的做法,如果你的游戏不需要如此复杂的方法,或是你对此方法有成见的话就不要做了。也许,我真的有点偏离我的初衷,不像是在给我的游戏实现一个UI系统了。如果你想为类似DOS的系统添加一个GUI的话,嘿嘿,X-Window那样子的。按本文的UI系统做也许会有点小用。
对于消息映射和命令绕行,我说不好,看了两天《深入浅出MFC》,才稍微有点明白,建议你自己去看。我只说一点不同之处,游戏中MFC的方法不是完全适用的。MFC是一个应用程序框架,MFC命令绕行的过程控制的是程序的全部,而游戏的UI系统只是一个辅助部分,不是全部,甚至可以去掉(我是说做到游戏里去,我猜95仙剑的菜单就是和游戏一体的)。UI系统要根据鼠标键盘做出不同的反应,绘制不同的界面,它又不是程序的全部,所以你需要把每一个可用(有用)的消息都传给UI系统。(不一定是全部的消息,比如说我的UI只使用鼠标操作,那就只传递WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEMOVE、WM_LBUTTONDBLCLK等等)
while(1)
{
if(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)
{
if(!GetMessage(&msg,NULL,0,0))
break;
if(!TranslateAccelerator(msg.hwnd,hAccelTable,&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// 在这里添加你向UI系统传递消息的代码
UI_DecipherMessage(&msg);
}
else
{
// 在这里添加你游戏的主要逻辑代码
GameMain();
}
}
然后,做一个仅使用在UI系统内部的消息映射和命令绕行,它处理的是UI系统自己产生的消息,或是改变一些窗口的显示与关闭,或是向游戏主要逻辑做出如使用物品、退出游戏之类的信号。是的,UI系统需要自己产生很多消息,不要指望windows会主动因为你的控件被按下而向消息队列里传送WM_COMMAND消息,你得自己做这些,使用PostMessage函数就可以了。有时候工作会更多,因为你面对的是windows的消息队列和UI系统自身的消息队列。
5、一个例子
HOHOv5中的UI系统作为这篇文章的例子再合适不过了,可以说我的思路可能是被它干扰了。
在下一篇中,我可能会讲讲本文未能说清楚的问题和一个不用消息映射及命令绕行的实例。
参考:
(1)HOHOv5的UI系统头文件(没办法,我没有任何一个版本的源文件,也没厚着脸皮去找人家要。嘿嘿,关键是没法要。写这篇文章的时候我的网还没有办下来,怎么到我这里就预留接口不足,气死了)
(2)《深入浅出MFC》第一章的讯息映射的雏形、第三章的讯息映射和命令绕行、第九章的全部
(3)MSDN和winuser.h
(4)窗口之父CXWnd的封装