Godot引擎 4.0 文档 - 循序渐进教程 - 使用信号

本文为Google Translate英译中结果,DrGraph在此基础上加了一些校正。英文原版页面:

Using signals — Godot Engine (stable) documentation in English

使用信号

在本课中,我们将研究信号。它们是节点在发生特定事件时发出的消息,例如按下按钮。其他节点可以连接到该信号并在事件发生时调用函数。

信号是 Godot 中内置的一种委托机制,它允许一个游戏对象对另一个游戏对象的变化做出反应,而无需它们相互引用。使用信号限制耦合并保持代码的灵活性。

例如,您可能在屏幕上有一个代表玩家生命值的生命条。当玩家受到伤害或使用治疗药水时,您希望条形图反映这种变化。为此,在 Godot 中,您将使用信号。

注:正如介绍中提到的,信号是 Godot 版本的观察者模式。您可以在这里了解更多信息: https: //gameprogrammingpatterns.com/observer.html

现在,我们将使用一个信号让上一课(监听玩家输入)中的 Godot 图标通过按下按钮移动和停止。

场景设置

为了向我们的游戏添加一个按钮,我们将创建一个新的主场景,其中将包含一个按钮和我们在之前课程中编写的场景Sprite2D.tscn

通过转到菜单 Scene -> New Scene 创建一个新场景。

在场景停靠栏中,单击 2D 场景按钮。这将添加一个 Node2D 作为我们的根。

扫描二维码关注公众号,回复: 15574360 查看本文章

在 FileSystem 停靠栏中,单击您之前保存的文件Sprite2D.tscn并将其拖动到 Node2D 上以对其进行实例化。

我们想添加另一个节点作为 Sprite2D 的兄弟节点。为此,右键单击 Node2D 并选择添加子节点。

搜索 Button 节点类型并添加它。

默认情况下,该节点很小。单击并拖动视口中按钮右下角的手柄以调整其大小。

如果您没有看到手柄,请确保选择工具在工具栏中处于活动状态。

单击并拖动按钮本身,使其更靠近精灵。

您还可以通过在 Inspector 中编辑其 Text 属性来在 Button 上写一个标签。输入“切换动作”【Toggle motion】。

您的场景树和视口应如下所示。

保存新创建的场景。然后您可以使用F6(在 macOS 上Cmd + R) 运行它。此刻,该按钮将可见,但如果您按下它,目前不会发生任何事情。

在编辑器中连接信号

在这里,我们想要将按钮的“按下”信号连接到我们的 Sprite2D,并且我们想要调用一个新函数来打开和关闭它的运动。我们需要将脚本附加到 Sprite2D 节点,这是我们在上一课中所做的。

您可以在 Node dock 中连接信号。选择 Button 节点,然后在编辑器的右侧,单击 Inspector 旁边名为“Node”的选项卡。

停靠栏显示所选节点上可用的信号列表。

双击“按下”【pressed】信号打开节点连接窗口。

在那里,您可以将信号连接到 Sprite2D 节点。该节点需要一个接收方方法,即当 Button 发出信号时 Godot 将调用的函数。编辑器为您生成一个。按照惯例,我们将这些回调方法命名为“_on_node_name_signal_name”。在这里,【可以命名为】“_on_button_pressed”。

注:当通过编辑器的 Node dock 连接信号时,您可以使用两种模式。简单的只允许您连接到附加有脚本的节点,并在它们上创建一个新的回调函数【上图】。

高级视图允许您连接到任何节点和任何内置函数,向回调添加参数,并设置选项。您可以通过单击在窗口右下角“高级【Advanced】”按钮切换模式。

点击Connect按钮完成信号连接,跳转到Script工作区。您应该会在左边距中看到带有连接图标的新方法。

如果单击该图标,将弹出一个窗口并显示有关连接的信息。此功能仅在编辑器中连接节点时可用。

让我们用切换节点运动的代码替换带有关键字pass的行。

我们的 Sprite2D 在_process()函数中移动了。Godot 提供了一种打开和关闭【_process函数】处理的方法:Node.set_process()。Node 类的另一个方法, 如果当前处于空闲状态,is_processing()则返回true。我们可以使用not关键字来取得其反转值。

GDScript
func _on_button_pressed():
    set_process(not is_processing())

此功能将切换处理【__process】,进而在按下按钮时打开和关闭图标的动作。

在尝试游戏之前,我们需要简化我们的_process()功能以自动移动节点而不是等待用户输入。将其替换为我们在两节课前看到的以下代码:

GDScript
func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta

您的完整sprite_2d.gd代码应如下所示。

GDScript
extends Sprite2D

var speed = 400
var angular_speed = PI


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())

现在运行场景并单击按钮以查看精灵开始和停止。

通过代码连接信号

您可以通过代码而不是使用编辑器连接信号。当您在脚本中创建节点或实例化场景时,这是必需的。

让我们在这里使用不同的节点。Godot 有一个Timer节点,可用于实现技能冷却时间、武器重新加载等。

返回 2D 工作区。您可以单击窗口顶部的“2D”文本或按Ctrl + F1(在 macOS 上Alt + 1)。

在场景停靠栏中,右键单击 Sprite2D 节点并添加一个新的子节点。搜索 Timer 并添加相应的节点。您的场景现在应该如下所示。

选择 Timer 节点后,转到 Inspector 并启用Autostart 属性。

单击 Sprite2D 旁边的脚本图标以跳回脚本工作区。

我们需要做两个操作来通过代码连接节点:

  1. 从 Sprite2D 获取对计时器的引用。

  2. connect()在定时器的“超时”信号上调用该方法。

注:要通过代码连接到信号,您需要调用connect() 要收听的信号的方法。在这种情况下,我们要监听定时器的“超时”信号。

我们希望在实例化场景时连接信号,我们可以使用Node._ready()内置函数来实现,该函数在节点完全实例化时由引擎自动调用。

要获取相对于当前节点的节点引用,我们使用方法 Node.get_node()。我们可以将引用存储在变量中。

GDScript
func _ready():
    var timer = get_node("Timer")

该函数get_node()查看 Sprite2D 的子节点并通过名称获取节点。例如,如果您在编辑器中将 Timer 节点重命名为“BlinkingTimer”,则必须将调用更改为get_node("BlinkingTimer").

我们现在可以在函数中将 Timer 连接到 Sprite2D _ready()

GDScript
func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)

该行内容如下:我们将定时器的“超时”信号连接到脚本所附加的节点。当 Timer 发出时timeout,我们要调用函数_on_timer_timeout(),该函数是我们目前需要定义的函数。让我们将它添加到脚本的底部并使用它来切换精灵的可见性。

注:按照惯例,我们将这些回调方法在 GDScript 中命名为“_on_node_name_signal_name”,在 C# 中命名为“OnNodeNameSignalName”。在这里,GDScript 为“_on_timer_timeout”,C# 为 OnTimerTimeout()。

GDScript
func _on_timer_timeout():
    visible = not visible

visible属性是一个布尔值,用于控制节点的可见性。该行visible = not visible切换值visible。如果是true,则变为false,反之亦然。

如果现在运行场景,您会看到精灵以一秒的间隔闪烁。

完整脚本

这就是我们移动和闪烁的 Godot 图标小演示!这是完整的sprite_2d.gd文件以供参考。

GDScript
extends Sprite2D

var speed = 400
var angular_speed = PI


func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())


func _on_timer_timeout():
    visible = not visible

自定义信号

注:本节是关于如何定义和使用您自己的信号的参考,而不是建立在之前课程中创建的项目的基础上。

您可以在脚本中定义自定义信号。例如,当玩家的生命值达到零时,您想要显示游戏结束屏幕。为此,您可以定义一个名为“死亡”或“健康耗尽”的信号,当它们的健康状况达到 0 时。

GDScript
extends Node2D

signal health_depleted

var health = 10

注:由于信号代表刚刚发生的事件,我们通常在其名称中使用过去式的动作动词。

您的信号与内置信号的工作方式相同:它们出现在“节点”选项卡中,您可以像连接任何其他信号一样连接到它们。

要在脚本中发出信号,请调用emit()信号。

GDScript
func take_damage(amount):
    health -= amount
    if health <= 0:
        health_depleted.emit()

信号可以选择性地声明一个或多个参数。在括号之间指定参数名称:

GDScript
extends Node

signal health_changed(old_value, new_value)

var health = 10

注:信号参数显示在编辑器的节点停靠栏中,Godot 可以使用它们为您生成回调函数。但是,您仍然可以在发出信号时发出任意数量的参数。所以由你来发出正确的值。

要随信号发出值,请将它们作为额外参数添加到函数中 emit()

GDScript
func take_damage(amount):
    var old_health = health
    health -= amount
    health_changed.emit(old_health, health)

总结

Godot 中的任何节点都会在特定事件发生时发出信号,例如按下按钮。其他节点可以连接到单独的信号并对选定的事件出反应 。

信号有很多用途。有了它们,您可以对进入或退出游戏世界的节点、碰撞、角色进入或离开区域、界面元素大小变化等等做出反应。

例如,每当玩家的物理身体进入其碰撞形状时,表示硬币 的Area2Dbody_entered就会发出信号,让您知道玩家何时收集了它。

在下一节“您的第一个 2D 游戏”中,您将创建一个完整的 2D 游戏并将到目前为止所学的一切付诸实践。

【DrGraph】:信号类似于Windows应用程序中的消息,而且更为简单。从Godot的设计思路来看,信号只是将本对象(节点、脚本、场景...)的某些消息暴露给本对象的父对象调用,这些消息是本对象的一些业务逻辑时机(时刻),若父对象绑定了这些信号,则在本对象的相应业务逻辑时刻会调用父对象的相应函数。因为是父对象内部调用,如果修改了父对象的一些属性等,就可以影响父对象的业务逻辑。

猜你喜欢

转载自blog.csdn.net/drgraph/article/details/130796381