点钞机厂家
免费服务热线

Free service

hotline

010-00000000
点钞机厂家
热门搜索:
技术资讯
当前位置:首页 > 技术资讯

如何为小型游戏创造简单的GUI系统1

发布时间:2021-01-19 18:34:47 阅读: 来源:点钞机厂家

如果你正在编写自己的游戏,迟早会用到一些用户界面(或者图像用户界面,即GUI)。我们有一些现成的库可以借用。也许最著名的当属CEGUI,它也很容易整合到OGRE 3D引擎中。行业中有许多关于如何处理“我应该使用什么GUI?”这个永恒问题的讨论。

不久前我遇到了一个相同的问题。我需要一个用C++编写,并且能够与OpenGL和DirectX共存的库。另一个条件就是支持经典的鼠标控制模式以及点触输入。CEGUI看似一个良好的选择,但其问题在于复杂性,以及不是很出色的点触支持。多数时候,我只需要一个简单的按钮,复选框,文本字幕以及“图标”或图像。用这些内容,你几乎可以创造任何东西。你可能知道,GUI并不仅局限于你所能看到的东西。它还包括功能,比如我点击一个按钮会发生什么情况,如果我在某个元素之上移动鼠标又会发生什么等等。如果你选择自己编写系统,你就需要植入这些,并考虑到其中某些东西仅适用于鼠标控制,有些元素仅能支持点触控制(例如一次点触多个地方)。

从头编写GUI并非易事。我将创造一些复杂而能够处理一切的系统。我们设计的GUI将用于静态游戏菜单控制和游戏内部菜单。你可以用这个系统展示得分,生命和其他信息。图1和图2就是用这个简单系统所创造的例子。这些是我所创造的引擎和游戏截图。

图1

图2

我们的GUI将用C++来创造,可用于OpenGL或DirectX。我们并不使用任何特定的API。除了简单的C++,我们还需要一些库令操作更便捷。FastDelegate是最棒的“库”之一,将可用于功能指针(我们将用它作为触发器)。至于字体渲染,我决定使用FreeType2库。这有一点复杂,但却值得一试。它可以满足你所有的需求。如果你愿意,还可以添加一些不同图像格式(如jpg、png……)的支持。为了简便起见,我使用了tga和png(虽然没有那么快,但容易使用LodePNG库)。

现在让我们来讨论细节内容吧。

本文分为以下三个章节:

第1部分:定位

第2部分:控制逻辑

第3部分:渲染

坐标系统和定位

每个GUI系统最重要的东西都是屏幕上的元素定位。图像API使用范围为[-1, 1]的屏幕空间坐标。它并无不妥,但如果我们是要创造/设计GUI就没有那么好了。

该如何决定一个元素的定位?有一个选择是使用一个绝对系统,设置元素的真正像素坐标。这很简单但并不实用。如果我们更改了分辨率,我们的整个设计美观的系统就会被搞砸。第二个方法是使用一个相对系统。它效果更好,但也不是100%完美。如果我们想让一些控制元素位于屏幕下方,带有一点位移。如果使用了相对坐标,其空间就会因分辨率而产生变化,而这并非我们想要的结果。

最后我结合使用了两种方法。你可以在相对和绝对坐标中设置位置。这与CEGUI中所用的解决方案有点相似。

以上所描述步骤适用于整个屏幕。在GUI设计中,只有极少数元素能够满足这个条件。多数情况下,我们要创造一些面板,并将其他元素布置在这个面板上。为什么?答案很简单。如果我们移动了面板,所有元素都会随之移动。如果我们隐藏面板,所有元素也会随之隐藏。

目前我所编写的定位内容正适合这种情况。结合使用了相对与绝对定位,但这一次相对起点并非整个屏幕的[0,0]角落,而是我们“面板”的[0,0]。这个[0,0]点在屏幕上已经有一些坐标,但对我们来说并没有什么意义。用一张图就足以说明一切了:

图3

(图3:元素定位。黑色代表屏幕(主窗口)及其内部元素。面板位于屏幕内部。绿色元素位于面板内部。其位置在面板范围之内。)

除了移位困难之外,这也是我有意将每个位置以像素而非相对[0, 1]坐标存储的主要原因。我曾经计算过像素位置,我并不需要根据一个元素的母元素和真正的像素位移进行数次的百分比重复计算。这种方法的缺点在于如果屏幕大小变了,整个GUI都得重新载入。说实话,我们的确需要频繁更改图像应用分辨率。

如果我们使用一些图像API(OpenGL或者DirectX),就是在屏幕空间而非像素中进行渲染。屏幕空间与百分比相似,但具有[-1,1]的范围。在向GPU上传数据之前,我们最后一个步骤就是转换屏幕空间坐标。以下三个等式呈现了转换点到屏幕空间坐标的管道。根据屏幕分辨率,通过简单的划分像素位置可以将输入像素转化为[0, 1]中的点。在[0, 1]中的点,我们将其映射到[-1, 1]。

pixel = [a, b]

point = [pixel(a) / width, pixel(b) / height]

screen_space = 2 * point – 1

如果我们想使用没有图像API的GUI,那就要使用Java,C#……并将元素绘成图像,你可以继续使用象素。

锚定系统

现在开始情况会更有趣一点。GUI设计的一大好处就在于使用锚定。如果你曾创造过GUI,你就知道什么是锚定了。如果你想让自己的元素无论屏幕大小都能坚守屏幕的某个部分,那就必须进行锚定。我决定使用相似但略微不同的系统。我所拥有的每个元素都有其自身起源。这可能是四个角落之一(注:左上角-TL,右上角-TR,左下角BL,右下角-BR)或者它的中心-C。你所进入的位置与这个起源有关。默认系统是TL。

假设你想让自己的元素总是居于屏幕右下角。你可以用TL起源模拟这个位置及其元素大小。一个更好的解决方法就是倒退。在一个带有变化起源的系统中定位元素,并在之后将其转化到TL起源(详见代码)。这有一个优势:你将保持GUI定义的统一性,并且它更易于维护。

统一

在以下代码中,你将看到来自用衣输入内部元素坐标系统的完整计算和转换,它使用的是象素。首先,我们要计算由我们GUI用户所提供的象素位置角落。我们还必须计算元素宽度和高度(我们将在之后的章节讨论元素比例的问题)在此,我们需要母体的比例——即它的大小和TL角落象素坐标。

float x = pLeft.X;

x += pos.x * dth;

x += fsetX;

float y = pLeft.Y;

y += pos.y * ight;

y += fsetY;

float w = dth;

w *= dim.w;

w += xelW;

float h = ight;

h *= dim.h;

h += xelH;

现在,我们已经计算了指定角落的象素位置。但是,我们系统人内存必须统一,所以一切都要转换到TL中含有[0,0]的系统。

//change position based on origin

if (igin == TL)

{

//do nothing – top left is default

}

else if (igin == TR)

{

x = tRight.X – (x – pLeft.X); //swap x coordinate

x -= w; //put x back to top left

}

else if (igin == BL)

{

y = tRight.Y – (y – pLeft.Y); //swap y coordinate

y -= h; //put y back to top left

}

else if (igin == BR)

{

x = tRight.X – (x – pLeft.X); //swap x coordinate

y = tRight.Y – (y – pLeft.Y); //swap y coordinate

x -= w; //put x back to top left

y -= h; //put y back to top left

}

else if (igin == C)

{

//calculate center of parent element

x = x + (tRight.X – pLeft.X) * 0.5f;

y = y + (tRight.Y – pLeft.Y) * 0.5f;

x -= (w * 0.5f); //put x back to top left

y -= (h * 0.5f); //put y back to top left

}

//this will overflow from parent element

pLeft = MyMath::Vector2(x, y);

tRight = MyMath::Vector2(x + w, y + h);

dth = w;

ight = h;

根据以上代码,你可以使用几乎相同的用户代码轻松在母元素的每个角落定位元素。我们使用float而不是int来代表象素坐标。这是可以的,因为最终我们会把它转变为屏幕空间坐标。

比例

当我们确立一个元素的坐标时,我们还必须知道它的大小。你可能还记得,我们需要计算比例来计算元素的位置,现在我们就来深入讨论这个话题。

比例与定位极为相似。我们再次使用相对和绝对衡量方法。相对数字会给你一个相当于母元素百分比的大小以及象素补偿。我们必须记住一件重要的东西——纵横比(AR,也称屏幕宽高比)。我们想让自己的元素任何时间都保持原样。如果我们的图标在一个系统上是正确的,但在另一系统上却变样了,那就不妙了。我们可以通过指定一个维度(宽度或高度)以及这个维度的相对纵横比来修复这个问题。

查看以下例子的不同之处:

a) – create element of size 10% of parent W

b) – create element of size 10% of parent W and H

这两者都可创造一个相同宽度的元素。a)选择将总含有正确的AR,而b)选择的大小总是与其母元素相同。

在使用相对大小时,最好是以象素设置一种最大限度的元素大小。我们想让一些元素在小屏幕上尽量更大一些,但在大屏幕上又不能过大。这方面的典型例子就是手机和平板电脑。没有必要让一个元素在平板电脑上显得过大(例如100*100象素)。它可以选择50*50,这个大小就够了。但在更小的屏幕上,就要尽量根据用户输入的相对大小来选择。

字体

我们要特别注意字体的问题。这里的定位和比例与典型的GUI元素有所不同。首先,字体定位通常最好将起源放置于中央。这样,我们就可以轻松将母元素内部的字体放置在中间,例如按钮。正如之前所言,要重新计算来自二手系统进入含TL起源系统的位置,我们就必须知道元素的大小。

图5

(图5:为中间字体定位的母元素中间起源)

这是一个棘手的环节。当我们处理文本时,我们只设置高度,而宽度则取决于多种因素——例如已用字体、字体大小、印刷文本等。当然,我们可以手动计算大小并在过后使用,但这种做法并不正确。在运行时间中,文本可能发生变化(例如我们游戏中的积分),那么之后会发生什么情况呢?一个更好的方法就是重新计算基于文本变化的位置(文本、字体或字体大小等因素的变化)。

我之前曾提到,我针对字体使用的是FreeType库。这个库可以根据已用字体为每个字母产生单张图片。这无关我们是否事先在字体集纹理中生成这些图像,也无关我们是否在忙碌中创造了这些图像。要计算文本比例我们并不需要真正的图像,只需要其大小。问题在于我们想展示的整个文本大小。这必须通过迭代每个字符和累加比例,及其它们每一者的空间来计算。我们还必须注意新的线段。

在处理文本时你必须考虑一件事情。请看下图及其字幕。有人可能会认为其问题的答案很明显,但我在设计和编码时却没有及时注意在这一点,所以它给我带来了很大麻烦。

图6

(图6:文本渲染背景:起源在TL,高度设置为100%的母高度。你可能会注意到,文本并没有布满整个空间,为什么?)

答案很简单。文本中有一些很容易辨识并且计算到整体大小中的标记。这里还应该为派生者保留空间,但它们并不用于我所使用的大写字母。你所需要注意的一切都已呈现在下图中:

图7

(图7:字体规则)

总结

我已经描述了自己设计过程所运用的定位和设置GUI元素大小的基本要领。也许还有更好或更复杂的操作方法。这里只是一个简单的方法,并且我在运用的过程中没有遇到任何问题。

心水至尊

365娱乐app

御剑仙缘手游

斩仙录OL