第4部分 用户界面
您的游戏制作的最后一块是用户界面(UI)。这是一个界面,用于显示玩家在游戏过程中需要查看的信息。在游戏中,这也被称为平视显示器(HUD),因为信息在游戏视图的顶部显示为叠加。您还将使用此场景显示开始按钮。
HUD将显示以下信息:
- 分数
- 剩余时间
- 消息,例如Game Over
- 开始按钮
节点设置
创建一个新场景并添加名为HUD的CanvasLayer节点。CanvasLayer节点允许您在游戏剩余部分上方的图层上绘制UI元素,这样它显示的信息就不会被玩家或硬币等任何游戏元素所覆盖。
Godot提供了各种各样的UI元素,可用于创建从健康栏等指标到库存等复杂界面的任何内容。事实上,你用来制作这个游戏的Godot编辑器是使用这些元素在Godot中构建的。 UI元素的基本节点从Control扩展,并在节点列表中以绿色图标显示。要创建UI,您将使用各种控制节点来定位,格式化和显示信息。以下是完成后HUD的样子:
锚点和边距
控制节点具有位置和大小,但它们也具有称为anchors(锚点)和margins(边距)的属性。锚定义节点边缘相对于父容器的原点或参考点。边距表示从控制节点的边缘到其对应锚点的距离。移动控件节点或调整控件节点大小时,边距会自动更新。
信息标签
将Label节点添加到场景并将其名称更改为MessageLabel。此标签将显示游戏的标题,以及游戏结束时的Game Over。此标签应以游戏屏幕为中心。您可以使用鼠标拖动它,但要精确放置UI元素,您应该使用Anchor属性。
选择View|Show Helpers显示帮助程序以显示可帮助您查看锚点位置的引脚,然后单击Layout菜单并选择HCenter Wide(水平居中):
MessageLabel现在跨越屏幕的宽度并垂直居中。 Inspector中的Text属性设置标签显示的文本。把它设置为Coin Dash!并将Align和Valign设置为Center。
Label节点的默认字体非常小,因此下一步是分配自定义字体。向下滚动到Inspector中的Custom Fonts部分,然后选择New DynamicFont,如以下屏幕截图所示:
现在,单击DynamicFont,您可以调整字体设置。从FileSystem侧边栏中,拖动Kenney Bold.ttf字体并将其放在Font Data属性中。将Size设置为48,如以下屏幕截图所示:
分数和时间显示
HUD的顶部将显示玩家的分数和时钟剩余的时间。这两个都是Label节点,排列在游戏屏幕的两侧。您可以使用Container节点来管理其位置,而不是单独定位它们。
Containers容器
UI容器自动排列其子Control节点(包括其他Containers)的位置。您可以使用它们在元素周围添加填充,居中,或按行或列排列元素。每种类型的Container都有特殊的属性来控制他们如何安排子项。您可以在检查器的Custom Constants部分中看到这些属性。
提示
请记住,容器会自动调整其子项。如果移动或调整Container节点内的Control,您会发现它会快速恢复到原始位置。您可以手动排列控件或使用容器排列它们,但不能同时排列两者。
MarginContainer节点添加到HUD。使用Layout菜单将锚点设置为Top Wide。在Custom Constants部分中,将Margin Right,Margin Top和Margin Left设置为10。这将添加一些填充,以便文本不会对着屏幕的边缘。
由于得分和时间标签将使用与MessageLabel相同的字体设置,因此如果您复制它,将节省时间。单击MessageLabel并按两次Ctrl + D(在macOS上为Cmd + D)以创建两个重复标签。拖动它们并将它们放在MarginContainer上以使它们成为子项。将一个命名为ScoreLabel,另一个命名为TimeLabel,并将Text属性设置为0。为ScoreLabel设置为Align to Left,为TimeLabel设置为Align to Right。
通过脚本更新UI
将脚本添加到HUD节点。例如,该脚本将在其属性需要更改时更新UI元素,并在收集硬币时更新分数文本。请参阅以下代码:
extends CanvasLayer
signal start_game
func update_score(value):
$MarginContainer/ScoreLabel.text = str(value)
func update_timer(value):
$MarginContainer/TimeLabel.text = str(value)
Main场景的脚本将调用这些函数,以便在值发生变化时更新显示。对于MessageLabel,您还需要一个计时器,使其在短暂的一段时间后消失。添加Timer节点并将其名称更改为MessageTimer。在Inspector中,将其Wait Time设置为2秒,然后选中One Shot设置为On。这确保了在启动时,计时器只运行一次,而不是重复。添加以下代码:
func show_message(text):
$MessageLabel.text = text
$MessageLabel.show()
$MessageTimer.start()
在此功能中,您将显示消息并启动计时器。要隐藏消息,请连接MessageTimer的timeout()
信号并添加以下内容:
func _on_MessageTimer_timeout():
$MessageLabel.hide()
使用按钮
添加一个Button节点并将其名称更改为StartButton。此按钮将在游戏开始前显示,点击后,它将隐藏自身并向Main场景发送信号以启动游戏。将Text属性设置为Start并更改自定义字体,就像使用MessageLabel一样。在Layout菜单中,选择Center Bottom。这会将按钮放在屏幕的最底部,因此可以通过按向上箭头键或编辑Margin并将Top设置为-150,将Bottom设置为-50来将其向上移动一点。
单击按钮时,会发出信号。在StartButton的Node选项卡中,连接pressed()
信号:
func _on_StartButton_pressed():
$StartButton.hide()
$MessageLabel.hide()
emit_signal("start_game")
HUD发出start_game信号以通知Main,是时候开始新游戏了。
游戏结束
UI的最终任务是对游戏结束作出反应:
func show_game_over():
show_message("Game Over")
yield($MessageTimer, "timeout")
$StartButton.show()
$MessageLabel.text = "Coin Dash!"
$MessageLabel.show()
在此函数中,您需要将Game Over消息显示两秒钟然后消失,这就是show_message()
所做的事情。但是,您还希望在消息消失后显示开始按钮。 yield()
暂停函数的执行,直到给定节点(MessageTimer)发出给定信号(timeout)。收到信号后,该功能继续,使您返回初始状态,以便您可以再次游戏。
文档
yield()
添加HUD到Main场景
现在,您需要设置Main场景和HUD场景之间的通信。将HUD场景的实例添加到主场景。在Main场景中,连接GameTimer的timeout()
信号并添加以下内容:
func _on_GameTimer_timeout():
time_left -= 1
$HUD.update_timer(time_left)
if time_left <= 0:
game_over()
每当GameTimer超时(每秒)时,剩余时间就会减少。接下来,连接Player的pickup()
和hurt()
信号:
func _on_Player_pickup():
score += 1
$HUD.update_score(score)
func _on_Player_hurt():
game_over()
游戏结束时需要做几件事,所以添加以下功能:
func game_over():
playing = false
$GameTimer.stop()
for coin in $CoinContainer.get_children():
coin.queue_free()
$HUD.show_game_over()
$Player.die()
此功能会暂停游戏,并循环显示硬币并删除剩余的任何内容,以及调用HUD的show_game_over()
函数。
最后,StartButton需要激活new_game()
函数。单击HUD实例并选择其new_game()
信号。在信号连接对话框中,单击Make Function将函数设置为Off,然后在Method In Node字段中键入new_game。这会将信号连接到现有功能,而不是创建新功能。看看下面的截图:
从_ready()
函数中删除new_game()
并将这两行添加到new_game()
函数中:
$HUD.update_score(score)
$HUD.update_timer(time_left)
现在,你可以玩游戏了!确认所有部件都按预期工作:分数,倒计时,游戏结束和重新启动等。如果您发现一块不起作用,请返回并检查您创建它的步骤,以及它与游戏其余部分连接的步骤。