设计GUI
现在,您已经掌握了基础知识,我们将了解如何构建具有可复用的UI组件的游戏图形用户界面(GUI):生命条、能量条、以及炸弹和翡翠计数器。在本教程结束时,您将拥有一个游戏GUI,可以使用GDscript或VisualScript进行控制:
最终效果
您还将学习:
- 创建灵活的UI组件
- 使用场景继承
- 构建一个复杂的UI
下载项目文件: ui_gui_design.zip
并解压缩存档。在Godot中导入 start/ 项目以遵循本教程。end/ 文件夹包含最终结果。
注解
You can watch this tutorial as a video on YouTube.
分解UI
让我们分解最终的UI并计划将要使用的容器。和 设计标题画面 一样,它以 MarginContainer
开头。然后,我们最多可以看到三列:
- 左边是生命和能量的计数器
- 生命和能量条
- 右侧是炸弹和翡翠的计数器
但是条的标签和标尺是同一个UI元素的两个部分。如果我们这样想的话,就剩下两列了:
- 左边是生命和能量条
- 右侧是炸弹和翡翠的计数器
这使嵌套容器变得更加容易:我们使用 MarginContainer
,在屏幕边框周围留有一些边距,然后使用 HBoxContainer
来管理两列。这两个条在 VBoxContainer
中叠加在一起。我们在右列中需要最后一个 HBoxContainer
,来并排放置炸弹和翡翠计数器。
我们只用4个容器就能得到干净的UI布局
我们将需要在各个UI组件内添加额外的容器,但这为我们提供了主GUI场景的结构。有了这个计划,我们可以进入Godot并创建我们的GUI。
创建基本GUI
GUI有两种可能的方法:我们可以在单独的场景中设计元素并将它们放在一起,或者在一个场景中对所有内容进行原型制作,然后将其分解。建议使用单个场景,因为这样可以更快地处理UI的位置和比例。一旦看起来不错,就可以将节点树的整个部分保存为可重用子场景。我们稍后就做。
现在,让我们从几个容器开始。
创建一个新场景并添加一个 MarginContainer
。选择节点并将其命名为 GUI
。
我们希望我们的界面锚定到屏幕的顶部。选择 GUI
节点并单击视图顶部的布局按钮。选择 Top Wide(顶部宽)
选项。默认情况下,GUI
节点将锚定到其父级的视图顶部。它将在垂直轴上自动调整大小,为其子UI组件腾出空间。
将场景另存为 GUI.tscn
。我们将整个GUI放入其中。
选择 MarginContainer
后,前往属性检查器并向下滚动到自定义常量部分。展开它,然后点击每个 Margin
属性旁边的字段。将它们全部设置为 20
像素。接下来,添加一个 HBoxContainer
节点。接下来,添加一个`HBoxContainer`节点。这一个将在左侧包含我们的两个条,并将它们与右侧的两个计数器分开。
我们想将条形图垂直堆叠在 HBoxContainer
中。添加一个 VBoxContainer
作为 HBoxContainer
的子项,并将其命名为 Bars
。再次选择父级 HBoxContainer
,这次添加另一个 HBoxContainer
作为其子级。称之为 Counters
。有了这四个容器,我们就为我们的GUI场景奠定了基础。
您应该有4个看起来像这样的容器
注解
之所以可以这样做,是因为我们首先分解了UI设计,并花了一些时间来考虑要使用的容器。当您学习这样的教程时,可能会觉得很奇怪。但是一旦您在真正的游戏中工作,您就会发现它是一种高效的工作流程。
创建条形基础
每个条形图拆分为两个水平对齐的子元素:带有健康计数的标签在左侧,量规在右侧。再次,HBoxContainer
是完成这项工作的理想工具。选择 Bars
节点,并在其中添加一个新的 HBoxContainer
。将其命名为 Bar
。
标签本身至少需要三个节点:一个作为背景的 NinePatchRect
,在其顶部,我们将在左侧添加一个纹理,即 HP
或 EP
,并在右侧用来显示值的一个 Label
。我们可以根据需要嵌套 Control
节点。我们可以将 NinePatchRect
用作其他两个元素的父元素,因为它包含了它俩。通常,您想使用容器代替,因为它们的作用是帮助组织UI组件。无论如何,我们以后都需要一个 MarginContainer
来在生命计数和量规之间增加一些空间。选择 Bar
并添加一个 MarginContainer
。将其命名为 Count
。在其中添加三个节点:
- 一个名为
Background
的NinePatchRect
- 一个名为
Title
的TextureRect
- 和一个名为
Number
的Label
要将节点添加为兄弟节点,请始终先选择 Count
节点。
您的场景树应如图所示。我们准备加入一些纹理
我们的场景仍然是空的。是时候加入一些纹理了。要加载纹理,请转到视图左侧的文件系统停靠面板。向下浏览到 res://assets/GUI
文件夹。
您应该会看到我们将用来为界面蒙皮的纹理列表。
在场景停靠面板中选择 Background
。在属性检查器中,您应该看到一个 Texture
属性。在 FileSystem
选项卡中,点击并将 label_HP_bg.png
拖到 Texture
插槽中。它保持压扁。父 MarginContainer
将强制其大小降为0,直到我们强制容器中的元素具有最小大小。选择 Background
节点。在属性检查器面板中,向下滚动到 Rect
部分。设置 Min Size
为(100,40)。您应该会看到 Background
和它的父容器一起重调大小。
接下来,选择 Title
并将 label_HP.png
拖放到其 Texture
插槽中。选择 Number
节点,点击 Text
属性旁边的字段,然后键入 10
。这样,我们可以在视图中看到两个节点。他们应该堆叠在其父级 MarginContainer
的左上角。
如果您选择两个节点,您应该会看到这样的结果
As they have a container as their direct parent, we cannot move them freely: the Count
node will always reset their anchors, their size and position. Try to move and resize the nodes in the viewport. Then, select any of the three textures and press Ctrl + Up or Ctrl + Down to reorder them in the Scene dock. They’ll snap back to their previous size and position.
父容器控制其直接子代的大小、比例、边距和锚点。若要修改节点,必须将它们嵌套在常规 Control
或另一个UI元素中。我们将使用 Background
作为 Title
和 Number
的父元素。选择 Title
和 Number
,然后拖放到 Background
上。
通过将 Background
节点用作两个纹理的父级,我们将控制权从 Count
、即 MarginContainer
节点移开
选择 Title
,然后在属性检查器中,将其 拉伸模式(Stretch Mode)
属性更改为 保持居中(Keep Centered)
。接下来,在属性检查器中找到 Rect
类别,并将 Size
属性更改为 (50, 40),使其仅占据背景的左半部分。接下来,选择 Number
节点。在视图中,点击 Layout
菜单并点击 Full Rect
。节点将调整大小以适合 Background
。前往属性检查器并将其 Align
属性更改为 Right
,将 Valign
属性更改为 Center
。文本应对齐到 Background
右边缘的中心。水平调整节点的大小,因此它占据了 Background
的右半部分,并且右边缘有一些填充。
这是节点的边界框在视图中的外观。保持粗糙,您现在不需要将它们放置得过于精确。
替换 Label
的字体
标签字体太小。我们需要更换它。选择 Number
节点,然后在属性检查器中,向下滚动到 Control
类,然后找到 Custom Font
类别。点击 Font
属性旁边的字段,然后点击 新建动态字体
。再次点击该字段,然后选择编辑。
您将输入 动态字体(Dynamic Font)
资源。展开 字体(Font)
类别,然后点击 字体数据(Font Data)
旁边的字段。点击 加载(Load)
按钮。在文件浏览器中,向下浏览到 assets/font
文件夹,然后双击 Comfortaa-Bold.ttf
将其打开。您应该在视图中看到字体更新。展开设置类别以更改字体大小。将 Size
属性设置为较高的值,例如 24
或 28
。
现在,我们需要文本的基线,即数字的下边缘,以与左侧的 HP
纹理对齐。为此,仍在 DynamicFont
资源中,您可以在 Extra Spacing
类别下调整 Bottom
属性。它为文本添加了一些底部填充。点击场景选项卡中的 Number
节点,返回到节点属性,然后将 Valign
更改为 Bottom
。要调整文本的基线,请再次单击 自定义字体(Custom Font)
类别下的 字体
字段,并调整 底部(Bottom)
属性,直到文本与 标题(Title)
节点对齐。这里使用了 2
像素的值。
底部值为2像素,数字与标题对齐
至此,我们完成了GUI中最难的部分。恭喜您!让我们继续讨论更简单的节点。
添加进度条
我们需要最后一个元素来完成我们的生命条:量规本身。Godot附带了一个 TextureProgress
节点,该节点具有我们所需的一切。
选择 Bar
节点,并在其中添加 TextureProgress
。命名为 Gauge
。在属性检查器中展开 纹理(Textures)
部分。转到 FileSystem
停靠面板,然后将 lifebar_bg.png
纹理拖放到 Under
插槽中。对 lifebar_fill.png
图像执行相同的操作,然后将其拖放到 Progress
插槽中。在属性检查器面板的 Range
类下,将 Value
属性更改为 50
,以查看量规是否被填满。
只有五个 Control
节点,我们的第一个条可以使用了。
就是这样,我们的生命条已准备就绪。最后一部分很快,不是吗?这要归功于我们强大的容器设置。
设计炸弹和翡翠计数器
炸弹和翡翠计数器就像条形图的 Count
节点一样。因此,我们将其复制并用作模板。
Under the Bar
node, select Count
and press Ctrl + D to duplicate it. Drag and drop the new node under the Counters
HBoxContainer
at the bottom of the scene tree. You should see it resize automatically. Don’t worry about this for now, we’ll fix the size soon.
将 Count2
节点重命名为 Counter
。与条形图不同,我们希望数字在左边,图标在右边。设置是相同的:我们需要一个背景( NinePatchRect
)、标题、和数字节点。Title
节点是一个 TextureRect
,这就是我们需要用来显示图标。在场景树中,选择 Title
节点,并将其重命名为 Icon
。
到目前为止,这是您的节点树的外观
选中 Icon
节点后,在属性检查器中,滚动到顶部以查看 Texture
插槽。转到左侧的 FileSystem
停靠面板,然后选择 bombs_icon.png
。将其拖放到 Texture
插槽中。在场景选项卡中,选择 Icon
和 Number
节点。点击视图顶部工具栏中的 Layout
菜单,然后选择 全角(Full Rect)
。两个节点都将更新以适合 背景(Background)
的大小。
节点锚定到整个背景,但是它们的位置不正确
Let’s change the Number
‘s align properties to move it to the left and center of the Background
. Select the Number
node, change its Align
property to left and the Valign
property to center. Then resize its left edge a bit to add some padding between the left edge of the Background
and the text.
The Number node aligned to the left and center
要使 Icon
和背景叠加,我们需要进行一些调整。首先,我们的背景有点高。这是因为它位于由最顶层GUI节点控制的边距容器内。选择场景树顶部的GUI节点,并垂直缩小其尺寸,以使其尽可能薄。您会看到仪表防止您把它做得太小。容器不能小于其子容器的最小大小。容器的边距也很重要。
选择图标,点击布局菜单,然后选择 全矩形(Full Rect)
以重新居中。我们需要它锚定到 Background
的右边缘。再次打开 Layout
菜单并选择 右居中(Center Right)
。向上移动图标,使其垂直居中于 Background
。
炸弹图标锚定在 背景(Background)
的右边缘。调整 计数器(Counter)
容器的大小,以查看粘在其右侧的 图标(Icon)
节点
因为我们从条形图的 Count
中复制了 Counter
,所以 Number
节点的字体关闭了。再次选择 Number
节点,转到 Font
属性,然后点击它访问 DynamicFont
资源。在 Extra Spacing
部分,将 Bottom
值改为 0
以重置字体的基线。我们的计数器现在运作正常。
让我们将 Counters
锚定到视图的右边缘。为此,我们需要将 Bars
容器设置为占用所有可用的水平空间。选择 Bars
节点,然后向下滚动到 Size Flags
类别。在 Horizontal
类别中,检查 Expand
值。Bars
节点应调整大小并将计数器推到屏幕右侧。
一个扩展的容器会占用其父容器的所有空间,推动沿途的其他一切东西
将条形图和计数器变成可复用的UI组件
我们有一个条形图和一个计数器小部件。但是我们每种都需要两个。稍后我们可能需要更改条形图的设计或功能。如果我们可以有一个场景来存储UI元素的模板,并让子场景来处理变体,那就太好了。Godot让我们通过继承的场景做到这一点。
让我们将 Counter
和 Bar
分支保存为单独的场景,将它们简化为创建 LifeBar
、 EnergyBar
、 BombCounter
、和 EmeraldCounter
。选择 Bar
HBoxContainer
。右键点击它,然后点击 将分支另存为场景(Save Branch as Scene)
。将场景保存为 Bar.tscn
。您应该看到节点分支将其变为单个 Bar
节点。
小技巧
场景是节点树。最顶部的节点是树的 根,层次结构底部的子级是 叶。除根节点以外的任何节点以及一个或多个子节点都是 分支。我们可以将节点分支封装到单独的场景中,或者将它们从其他场景加载并合并到活动场景中。右键点击场景停靠面板中的任何节点,然后选择 将分支另存为场景(Save Branch as Scene)
或 从场景合并(Merge from Scene)
。
然后,选择 Counter
节点并执行相同的操作。右键单击 将分支另存为场景(Save Branch as Scene)
,并将其保存为 Counter.tscn
。新的编辑场景图标将显示在场景树中节点的右侧。点击 Bar
旁边的一个以打开相应的场景。调整 Bar
节点的大小,使其边界框适合其内容。我们以命名和放置 Control
节点的方式,已经准备好继承此模板并创建生命条。同理操作 Counter
。
无需额外更改,我们的条形图即可使用
使用场景继承创建其余元素
我们需要两个条的工作方式相同:它们的左侧应有标签,并带有一些值,右侧应有水平量规。唯一的区别是一个带有 HP
标签,并且是绿色的,而另一个叫做 EP
,并且是黄色的。Godot为我们提供了一个强大的工具,可以为游戏中的所有条形图创建通用基础:继承的场景。
继承的场景可帮助我们保持GUI场景的整洁。最后,每个UI组件只有一个容器和一个节点。
在继承的场景上,除了名称之外,还可以更改属性检查器中每个节点的任何属性。如果修改并保存父场景,则所有继承的场景都会更新以反映所做的更改。如果您在继承的场景中更改值,它将始终覆盖父级的属性。这对于UI很有用,因为它们经常需要相同元素的变体。通常,在UI设计中,按钮、面板等具有相同的基本样式和交互。我们不想手动将其复制到所有变体中。
重新加载图标将出现在您重写的属性旁边。点击它可将值重置为父场景的默认值。
注解
将场景继承想像成节点树,或者在GDScript中使用 extends
关键字。继承的场景会像其父级一样执行所有操作,但是您可以覆盖属性、资源并添加额外的节点和脚本来扩展其功能。
继承 Bar
场景来构建生命条
Go to Scene -> New Inherited Scene
to create a new type of Bar
. Select the Bar scene and open it. You should see a new [unsaved] tab, that’s like your Bar
, but with all nodes except the root in grey. Press Ctrl + S (Cmd + S on macOS) to save the new inherited scene and name it LifeBar
.
您不能重命名灰色节点。这表示它们有一个父级场景
首先,将根节点或顶级节点重命名为 LifeBar
。我们始终希望根目录准确描述此UI组件是什么。该名称与接下来要创建的 EnergyBar
区别开来。场景内的其他节点应使用广义术语来描述组件的结构,因此它适用于所有继承的场景。就像我们的 TextureProgress
和 Number
节点一样。
注解
如果您曾经做过网页设计,则它与使用CSS的精神相同:创建基类,并使用修饰符类添加变体。在基本的按钮类中,您将具有绿色的按钮和红色的按钮供用户接受和拒绝提示。新类包含父元素的名称和一个额外的关键字,以说明其修改方式。创建继承的场景并更改顶级节点的名称时,我们正在做相同的事情。
设计 EnergyBar
我们已经使用主 Bar
场景设置了 LifeBar
的设计。现在我们需要 EnergyBar
。
让我们创建一个新的继承场景,然后再次选择 Bar.tscn
场景并将其打开。双击 Bar
根节点,并将其重命名为 EnergyBar
。保存新场景为 EnergyBar.tscn
。我们需要用 EP
的纹理替换 HP
纹理,并更改量规上的纹理。
转到左侧的 FileSystem
停靠面板,在场景树中选择 Title
节点,然后将 label_EP.png
文件拖放到纹理插槽中。选择 Number
节点,然后将 Text
属性更改为另一个值,例如 14
。
您会注意到 EP
纹理比 HP
纹理小。我们应该更新 Number
的字体大小以更好地适应它。字体是一种资源。使用此资源的整个项目中的所有节点都将受到我们更改的任何属性的影响。您可以尝试将大小更改为较大的值,例如 40
,然后切换回 LifeBar
或 Bar
场景。您将看到文本的大小增加了。
如果我们更改字体资源,则使用该字体资源的所有节点都会受到影响
要仅在此节点上更改字体大小,我们必须创建字体资源的副本。再次选择 Number
节点,并点击属性检查器面板右上角的扳手和螺丝刀图标。在下拉菜单中,选择 使子资源唯一(Make Sub-Resources Unique)
选项。Godot将找到该节点使用的所有资源,并为我们创建唯一的副本。
使用此选项为一个节点创建资源的唯一副本
小技巧
When you duplicate a node from the Scene tree, with Ctrl + D (Cmd + D on macOS), it shares its resources with the original node. You need to use Make Sub-Resources Unique
before you can tweak the resources without affecting the source node.
向下滚动到 自定义字体(Custom Font)
部分,然后打开 字体(Font)
。将 大小(Size)
降低到较小的值,例如 20
或 22
。您可能还需要调整 底部(Bottom)
间距值,以将文本的基线与左侧的 EP
标签对齐。
EP Count
小部件,其字体比 HP
同类小
现在,选择 TextureProgress
节点。拖动 energy_bar_bg.png
文件到 Under
槽,并对 energy_bar_fill.png
执行相同的操作。然后把它放到 Progress
纹理槽中。
您可以垂直调整节点的大小,以使其边界矩形适合量规。对 Count
节点执行相同的操作,直到其大小与条的大小对齐为止。因为 TextureProgress
的最小大小是根据其纹理设置的,所以您将无法缩小 Count
节点低于该值大小。这也是 Bar
容器将具有的大小。您也可以缩小此尺寸。
最后但并非最不重要的一点是,背景(Background)
容器的最小尺寸使其变得有点大。选择它,然后在 矩形(Rect)
部分中,将 最小大小(Min Size)
属性更改为 80
像素。它应该自动调整大小,并且 Title
和 Number
节点也应重新定位。
Count
看起来好多了,现在小了一点
小技巧
Count
节点的大小会影响 TextureProgress`
的位置。由于我们稍后将使条形图垂直对齐,因此最好使用 Counter
的左边距来调整 EP
标签的大小。这样,EnergyBar
的 Count
和 LifeBar
的 Count
节点都为 100
像素宽,因此两个量规将完美对齐。
准备炸弹和翡翠计数器
现在让我们来处理计数器。进入 场景 -> 新继承的场景(Scene -> New Inherited Scene)
,选择 Counter.tscn
作为基础。也将根节点重命名为 BombCounter
。将新场景保存为 BombCounter.tscn
。这就是这个场景的全部内容。
炸弹计数器与原始计数器场景相同
Go to Scene -> New Inherited Scene
again and select Counter.tscn
once more. Rename the root node EmeraldCounter
and save the scene as EmeraldCounter.tscn
. For this one, we mainly need to replace the bomb icon with the emerald icon. In the FileSystem tab, drag the emeralds_icon.png
onto the Icon
node’s Texture
slot. Icon
already anchors to the right edge of the Background
node so we can change its position and it will scale and reposition with the EmeraldCounter
container. Shift the emerald icon a bit to the right and down. Use the Arrow Keys on the keyboard to nudge its position. Save, and we’re done with all the UI elements.
翡翠计数器看起来应该像这样
将UI组件添加到最终的GUI
是时候将所有UI元素添加到主GUI场景中了。再次打开 GUI.tscn
场景,并删除 Bar
和 Counter
节点。在 FileSystem
停靠面板中,找到 LifeBar.tscn
并将其拖放到场景树中的 Bars
容器上。对 EnergyBar
执行相同的操作。您应该看到它们垂直对齐。
LifeBar
和 EnergyBar
自动对齐
现在,将 BombCounter.tscn
和 EmeraldCounter.tscn
场景拖放到 Counters
节点上。它们会自动调整大小。
调整节点大小以占用所有可用的垂直空间
为了让 EmeraldCounter
和 BombCounter
使用我们在 Counter.tscn
中定义的大小,我们需要在 Counters
容器上更改 Size Flags
。选择 Counters
节点,然后在属性检查器中展开 Size Flags
部分。取消选中 垂直(Vertical)
属性的 填充(Fill)
标记,然后选中 收缩中心(Shrink Center)
,以便容器在 HBoxContainer
内部居中。
现在,这两个计数器都有一个像样的大小
小技巧
更改 Counters
容器的 Min Size
属性,以控制计数器背景的高度。
我们在 EnergyBar
上留下一个带有 EP
标签的小问题:2个条应垂直对齐。点击 EnergyBar
节点旁边的图标以打开其场景。选择 Count
节点,然后向下滚动到 Custom Constants
部分。添加一个 20
的 Margin Left
。在 Rect
部分,将节点的 Min Size
设置为100,与 LifeBar
相同。Count
现在应该在左边有一些边距。如果您保存并返回GUI场景,它将与 LifeBar
垂直对齐。
2个条形图完美对齐
注解
我们原本可以这样设置 EnergyBar
。但是,这表明您可以随时返回任何场景进行调整,并查看更改在项目中的传播!
把GUI放到游戏的模型上
为了结束本教程,我们将GUI插入到游戏的模型场景中。
前往文件系统停靠面板,打开 LevelMockup.tscn
。
将 GUI.tscn
场景拖放到 bg
节点下方和 Character
上方。GUI将缩放以适合整个视图。前往 Layout
菜单并选择 Center Top
选项,使其锚定到游戏窗口的顶部边缘。然后调整GUI的大小,使其在垂直方向上尽可能小。现在,您可以看到界面在游戏中的外观。
祝贺您完成了这篇冗长的教程。最终项目您可以在 ui_gui_design.zip
找到。
最终效果
注解
A final note about Responsive Design. If you resize the GUI, you’ll see the nodes move, but the textures and text won’t scale. The GUI also has a minimum size, based on the textures inside of it. In games, we don’t need the interface to be as flexible as that of a website. You almost never want to support both landscape and portrait screen orientations. It’s one or the other. In landscape orientation, the most common ratios range from 4:3 to 16:9. They are close to one another. That’s why it’s enough for the GUI elements to only move horizontally when we change the window size.