Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

分数与重玩

在这一部分中,我们会添加计分、播放音乐、重启游戏的能力。

我们要用一个变量来记录当前的分数,使用最简的界面在屏幕上显示。我们会用文本标签来实现。

在主场景中,添加一个新的 Control 节点作为 Main 的子项,命名为 UserInterface。你会被自动切换到 2D 屏幕,可以在这里编辑你的用户界面 User Interface(UI)。

名为分数标签 ScoreLabelLabel

image1

检查器中将该 LabelText 设为类似“Score: 0”的占位内容。

image2

并且,文本默认是白色的,和我们的游戏背景一样。我们需要修改它的颜色,才能在运行时看到。

向下滚动到 Theme Overrides(主题覆盖)然后展开 Colors(颜色)并点击 Font Color(字体颜色)旁边的黑框来为文字着色

image3

最后单击视口中的文本,将其拖离左上角。

image4

UserInterface 节点让我们可以将 UI 组合到场景树的一个分支上,并且也让主题资源能够传播到它的所有子节点上。我们将会用它来设置游戏的字体。

创建 UI 主题

再次选中 UserInterface 节点。在检查器中为 Theme -> Theme 创建一个新的主题资源。

image5

单击这个资源就会在底部面板中打开主题编辑器。会展示使用你的主题资源时内置 UI 控件的外观。

image6

默认情况下,主题只有一个属性,Default Font(默认字体)。

参见

你可以为主题资源添加更多属性,从而设计更复杂的用户界面,不过这就超出本系列的范畴了。要学习主题的创建和编辑,请参阅 GUI 皮肤简介

这里需要的是一个字体文件,就是你电脑上用的那种。常见的字体文件格式有两种:TrueType 字体(TTF)和 OpenType 字体(OTF)。

文件系统面板中,展开 fonts 目录,单击我们包含在项目里的 Montserrat-Medium.ttf 文件并将其拖放到Default Font(默认字体)上。文本就又会出现在主题预览中了。

文本有一点小。将Default Font Size(默认字体大小)设置为 22 像素即可增大文本的大小。

image7

跟踪得分

我们下一步是进行计分。为 ScoreLabel 附加一个新的脚本,并在其中定义 score(分数)变量。

extends Label

var score = 0

每踩扁一只怪物,这个分数就应该加 1。我们可以使用它们的 squashed 信号来得知发生的时间。不过,因为我们是用代码实例化的怪物,我们无法在编辑器中将怪物的信号连接到 ScoreLabel

不过,我们可以在每次生成一只怪物时通过代码来进行连接。

打开 main.gd 脚本。如果它还开着,你可以在脚本编辑器左栏中点击它的名字。

image8

另一种方法是在文件系统面板中双击 main.gd 文件。

_on_mob_timer_timeout() 函数的最后添加下面这行代码:

func _on_mob_timer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

这一行的意思是,当小怪发出 squashed 信号时,ScoreLabel 节点就会接收到并调用 _on_mob_squashed() 函数。

回到 ScoreLabel.gd 脚本,定义回调函数 _on_mob_squashed()

这里我们将进行加分并更新显示的文本。

func _on_mob_squashed():
    score += 1
    text = "Score: %s" % score

第二行用 score 变量的值替换占位符 %s。使用此功能时,Godot 会自动将值转换为字符串文本,这在向标签中输出文本或者使用 print() 函数时非常方便。

参见

可以在 GDScript 格式字符串 学习字符串格式化相关的更多信息。在 C# 中请考虑使用“$”进行字符串插值

你现在可以玩游戏,压死几个敌人,看看分数的增长。

image9

备注

在一个复杂的游戏中,你可能想把你的用户界面与游戏世界完全分开。在这种情况下,你就不会在标签上记录分数了。相反,你可能想把它存储在一个单独的、专门的对象中。但当原型设计或你的项目很简单时,保持你的代码简单就可以了。编程总是一种平衡的行为。

重玩游戏

我们现在就要添加死亡后重玩的能力。玩家死亡后,我们会在屏幕上现实一条消息并等待输入。

回到 main.tscn 场景,选中 UserInterface 节点,添加 ColorRect 节点作为其子项并命名为 Retry(重试)。该节点会使用单一色彩填充矩形,我们用它来覆盖画面,达到变暗的效果。

要使其覆盖整个视口,可以使用工具栏中 锚点预设 菜单。

image10

点击打开,并应用整个矩形命令。

image11

什么都没发生。好吧,是几乎什么都没有;只有四个绿色的大头针移动到了选择框的四个角落。

image12

这是因为 UI 节点(图标都是绿色)使用的是锚点和边距,它们都相对于它们父节点包围框。这里的 UserInterface 节点比较小,所以 Retry 会受限于它。

选中 UserInterface 然后也对其使用锚点预设 -> 整个矩形Retry 节点就应该覆盖整个视口了。

让我们修改它的颜色,把游戏区域变暗。选中 Retry,在检查器中将 Color(颜色)设置为透明的暗色。要实现整个效果,可以在取色器中将 A 滑动条拖到左边。它控制的是颜色的 Alpha 通道,也就是不透明度。

image13

接下来,添加一个 Label 的节点作为 Retry 的子节点并且设置他的 Text 为“Press Enter to retry”。将其移动至屏幕中央,并且选择 Anchor Preset -> Center(锚点预设 > 居中)。

image14

编写重试选项

我们现在就可以去编写代码,在玩家死亡时显示 Retry 节点,重玩时隐藏。

打开 main.gd 脚本。首先,我们想要在游戏开始时隐藏覆盖层。将这一行加到 _ready() 函数中。

func _ready():
    $UserInterface/Retry.hide()

然后在玩家受到攻击时,我们就显示这个覆盖层。

func _on_player_hit():
    #...
    $UserInterface/Retry.show()

最后,当 Retry 节点可见时,我们需要监听玩家的输入,按下回车键时让游戏重启。可以使用内置的 _unhandled_input() 回调来实现,任何输入都会触发这个回调。

如果玩家按下了预设的 ui_accept 输入动作并且 Retry 是可见状态,我们就重新加载当前场景。

func _unhandled_input(event