信号-计时器例子
(开头惯例感谢godot开发者团队~~~~)
计时器例子
要查看信号是如何工作的,让我们尝试使用 Timer 节点。使用一个节点和两个子节点来创建一个新场景:一个是:Timer和一个 Sprite。您可以使用Godot图标作为Sprite的纹理,或者其他你喜欢的任意图像。
你的场景树应该是这样的:
在Timer节点的属性中,勾选*自动启动*旁边的选框。这会令计时器在你运行场景时自动启动。你可以将*等待时间*保留为1秒。
在 “检查器” 选项卡旁边是一个标记为 “节点” 的标签。单击此选项卡,您将看到所选节点可以发出的所有信号。在计时器节点的情况下,我们关心的是”超时”。每当计时器到达 0
时,就会发出这个信号。
点击 “timeout()” 信号,点击 “Connect…”。您将看到以下窗口,您可以在其中定义如何连接信号:
在左侧,您将看到你场景中的节点,并可以选择你想要“侦听”的节点信号。需要留意的是,定时器节点变红——这不是一个错误,但它是节点发出信号的视觉提示。选择根节点。
警告
目标节点*必须*附加一个脚本,否则您将收到一条错误消息。
在窗口的底部有一个“节点中的方法”标签。这是你在目标节点脚本中要使用的函数名。默认情况下,Godot将会按命名规则’ ‘ _on_<node_name>_<signal_name> ‘ ‘ ‘创建这个函数,但您可以按您的意愿更改它。
单击“Connect”,您将看到该函数已在脚本中创建:
extends Node
func _on_Timer_timeout():
pass # replace with function body
现在,我们可以将占位符代码替换为接收到信号时要运行的任何代码。让我们让精灵闪烁:
extends Node
func _on_Timer_timeout():
$Sprite.visible = !$Sprite.visible
运行这个场景,你会看到精灵每秒钟都在闪烁。您可以更改计时器的 等待时间 属性来改变这一点。
连接代码中的信号
您还可以在代码中进行信号连接而不是在编辑器中。当您通过代码实例化节点时,您并不能使用编辑器进行连接,因此这通常是必要的。
首先,在计时器的“节点”选项卡中选中’链接‘,并单击’断开连接‘来断开信号。
要在代码中进行连接,我们可以使用 connect
函数。我们将把它放在 _ready()
函数中,这样连接就会在运行时进行。函数的语法是 <source_node>.connect(<signal_name>, <target_node>, <target_function_name>)
。下面是我们的计时器连接的代码:
extends Node
func _ready():
$Timer.connect("timeout", self, "_on_Timer_timeout")
func _on_Timer_timeout():
$Sprite.visible = !$Sprite.visible
自定义信号
您也可以在Godot中声明您的的自定义信号:
extends Node
signal my_signal
一旦声明,你的自定义信号就会出现在检查器中,并且可以用与节点内置信号以相同的方式进行连接。
要通过代码发出信号,使用 emit
函数:
extends Node
signal my_signal
func _ready():
emit_signal("my_signal")
射击例子
作为信号使用的另一个例子,让我们考虑一个可以旋转并向鼠标射击的玩家角色。每次单击鼠标按钮,我们都会在玩家的位置创建子弹的实例。详情请参阅 实例化 。
然而,如果子弹作为玩家的子节点添加,那么当它旋转时,子弹将仍然保持“附着”在玩家身上:
相反,我们需要子弹独立于玩家的移动——一旦发射,子弹就会继续沿着直线运动,玩家就不能再影响它们了。与其作为玩家的孩子被添加到场景树中,不如将子弹作为“主”游戏场景的子节点添加上去更有意义,后者可能是玩家的父节点,甚至可能是更高层级的树。
你可以通过直接添加子弹来做到这一点:
var bullet_instance = Bullet.instance()
get_parent().add_child(bullet_instance)
然而,这将导致一个不同的问题。现在如果你尝试独立测试你的“游戏角色”场景,它会在射击时崩溃,因为没有父节点可以访问。这使得独立测试玩家角色代码变得更加困难,也意味着如果你决定改变主场景的节点结构,玩家的父节点可能不再是接收子弹的合适节点。
解决这个问题的方法是使用一个信号来“发射”玩家的子弹。玩家不需要“知道”子弹在那之后发生了什么——任何连接到信号的节点都可以“接收”子弹并采取适当的行动来产生它们。
下面是玩家使用信号发射子弹的代码:
extends Sprite
signal shoot(bullet, direction, location)
var Bullet = preload("res://Bullet.tscn")
func _input(event):
if event is InputEventMouseButton:
if event.button_index == BUTTON_LEFT and event.pressed:
emit_signal("shoot", Bullet, rotation, position)
func _process(delta):
look_at(get_global_mouse_position())
在主场景中,我们连接玩家的信号(它将出现在“Node”选项卡中)。
func _on_Player_shoot(Bullet, direction, location):
var b = Bullet.instance()
add_child(b)
b.rotation = direction
b.position = location
b.velocity = b.velocity.rotated(direction)
现在子弹将保持自己的运动独立于玩家的旋转: