设计日志三:3D游戏内的GUI系统

设计日志三:3D游戏内的GUI系统

3D游戏内往往无法使用操作系统的标准GUI,主要是由于GDI和3D是完全不同的加速模块处理的,如果把它们放在一起,会有闪烁、其中一个不可见等不确定的结果。另一方面,也有游戏程序普遍使用不同于系统的个性GUI的原因。游戏内的GUI可以做得简单、也可以做得复杂,但无论如何,它做起来总是比预想的复杂。

因为复杂,所以难以做得完备,大家往往更期望去利用现有的完备的系统,其中一个便是Flash。利用Flash最好的一点就是它有非常优秀的设计制作工具,最糟的一点就是它是纯软件的光栅化,在分辨率高的时候非常的慢。虽然Adobe宣称将开发使用硬件加速的版本,但至少目前还只是一张画的饼。另外一个选择是WPF,它在.NET 3.5 SP1版本里提供了集成D3D表面的功能。但看到这个系统需求多数人都不会再考虑它。剩下的选择还有找个开源的GUI引擎,比如CeGUI。可惜对我们中国人来说,这些GUI引擎几乎都有一个大问题——缺少对汉语的支持。所以,我们往往不得不自己制作GUI系统或者改造已有的系统。这是一个很大的话题,这里只讨论其中一二。

【文本】
从基本的文本说起。传统的渲染文本的方法是,将ASCII字母整齐排列在一张纹理上,然后绘制字符串里的每个字符对应的区域。后来为了支持字符不等宽字体,需要增加另外一个文件,记录每个字符的纹理坐标范围和将输出参考点移动到下一个字符位置的偏移量。需要这个偏移量是因为两个英文字符的间距不一定等于字符的宽度,而且严格的说这个偏移量只是个近似方案,因为英文的kerning其实是不同的字符组合间的间距都是不同的。所以我们在3D上绘制的文本细看的话都不如GDI画的好看。

对于汉语来说,就存在一个量变而导致的质变。扩展ASCII的256个符号对西方语言基本够用了,而现代常用汉字就有3000多个,GB2312的标准汉字有6000多个,GB18030-2000收录27000多汉字。而相对的,一般常用的汉字只有1000多个。如果把这些字符全部生成纹理,则浪费了大量的内存显存,而且80%的纹理几乎不会被用到。而如果我们只把少量常用的字符生成纹理,则用户会碰到有字打不出来的情况。另外,如果要支持繁体字的话,还需要更大的纹理,因为笔画多,小了看不清。如果又要考虑国际化的话,还会有很多更有趣的问题。比如,阿拉伯语和一些西方语言的字体,某些字符组合时要改变写法,而不是字符的简单连接。

再前进一步,又会遇到文本排版问题。英文的单词、数字等是不能在折行时切断的,汉语的句子在需要折行时也是不能把标点符号放到下一行首的,破折号、省略号在折行时不能被断开……(此处省略让人心里崩溃的几千字)

不言而喻,文本的渲染需要专门的优化。如果想偷懒的话,ID3DXFont是个很好东东,它会在用到的时候生成纹理,并缓存最近用到的字符,而且还处理了那个阿拉伯语和一些西方语言的字体的问题,虽然牺牲了点速度。另外一个省事的办法是把要渲染的整段文字画在纹理上一起显示,但缺点是对动态变化的文本需要频繁地更新纹理。所以,暂且先用ID3DXFont。

【窗口系统】
窗口系统想起来很简单,就是窗口的层层嵌套。这个东东好的地方就是让人非常容易理解,坏的地方是为了让它如此简单而不得不实现得非常复杂。

首先,顶级窗口存在叫做TopMost的特例,比如Tooltip、菜单、任何时候在游戏里按Esc出现的主选单。其次,鼠标输入和键盘输入的焦点是不同的。鼠标事件一般总是被正好在鼠标下的字窗口处理,但有个特例叫Mouse Capture,典型的出现在拖滚动条的时候。键盘输入总是直达某一焦点子窗口,但有个特例是快捷键,而快捷键在有输入焦点时又是必须屏蔽的。再次,Windows的传统窗口都是矩形的,但要做得炫的软件喜欢用不规则窗口,而游戏当然是要做炫的。还有鼠标悬停效果、拖拽效果等等问题。支持多少可以按需选择。

【布局系统】
早期的游戏基本上都忽略这个问题,因为那时显示器的分辨率大多是640x480或者800x600,后来一段时间增加了一些,但都是4:3的。而如今是4:3,16:9,5:4等各式各样。由此一来,布局成了不得不考虑的问题。

当前游戏的GUI系统多采用两种定位方式:绝对坐标和比例定位。比例定位就是用比例来确定坐标,比如角色信息窗口的坐标是X=10%, Y=20%, Width=40%, Height=60%,就是说左边在屏宽10%的位置,上边缘在屏高20%的位置,宽度为屏宽的40%,高度为屏高的60%。比例定位可以适应分辨率,但并不是总是得到合适的结果,所以往往结合绝对坐标。例如X=10%, Y=20%, Width=300, Height=600。当然,这只是简化问题的设计。用过WinForm、WPF的话,应该可以体会程序框架对解决布局问题的努力。有时候我们需要一个控件离父窗口的某一边保持固定的距离,有时候我们需要控件总是填充父窗口的某一区域。缩小的时候,有些控件有最小的极限限定,也有的有最大限制。还有一些专门的控件提供特殊的布局模式,如流方式布局、表格布局等。

毕竟我们的目标是做游戏,而不是优秀的GUI系统,只要选择能够满足需求的最简方案就够了。不过,个人并不喜欢比例定位,因为GUI元素大多不适合比例拉伸。比如一个按钮,即使分辨率改变,我们仍不希望按钮的纵横比被改变。再比如角色窗口显示在屏幕左半边10%处,在4:3时看起来很好,在16:9时会觉得位置怪怪的。所以,我打算使用简化的WinForm的布局模式,可以按对四个方向边界的相对位置停靠,这样可以实现非常灵活而又足够保真的布局。另外,再加一个整体的放缩系数,即可解决高低分辨率的差异。

【文本输入】
文本输入是个小问题,也可能是个大问题,因为独占全屏模式下,输入法的窗口是显示不出来的。玩过魔兽世界就应该注意到过,在全屏模式下会出现一个很另类的输入法选字窗口,而不是自己装的输入法的选字窗口。微软的Win32 API里,特别有一套IME API,要是想像魔兽一样在独占全屏模式也能输入中文的话,就只能学习IME API,自己绘制输入法选字窗口了。我就简单地偷懒,不用独占全屏模式,牺牲一点速度,节省大量的功夫。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值