信号-计时器例子

(开头惯例感谢godot开发者团队~~~~)

计时器例子

要查看信号是如何工作的,让我们尝试使用 Timer 节点。使用一个节点和两个子节点来创建一个新场景:一个是:Timer和一个 Sprite。您可以使用Godot图标作为Sprite的纹理,或者其他你喜欢的任意图像。

你的场景树应该是这样的:

../../_images/signals_node_setup.png

在Timer节点的属性中,勾选*自动启动*旁边的选框。这会令计时器在你运行场景时自动启动。你可以将*等待时间*保留为1秒。

在 “检查器” 选项卡旁边是一个标记为 “节点” 的标签。单击此选项卡,您将看到所选节点可以发出的所有信号。在计时器节点的情况下,我们关心的是”超时”。每当计时器到达 0 时,就会发出这个信号。

../../_images/signals_node_tab_timer.png

点击 “timeout()” 信号,点击 “Connect…”。您将看到以下窗口,您可以在其中定义如何连接信号:

../../_images/signals_connect_dialog_timer.png

在左侧,您将看到你场景中的节点,并可以选择你想要“侦听”的节点信号。需要留意的是,定时器节点变红——这不是一个错误,但它是节点发出信号的视觉提示。选择根节点。

警告

目标节点*必须*附加一个脚本,否则您将收到一条错误消息。

在窗口的底部有一个“节点中的方法”标签。这是你在目标节点脚本中要使用的函数名。默认情况下,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

运行这个场景,你会看到精灵每秒钟都在闪烁。您可以更改计时器的 等待时间 属性来改变这一点。

连接代码中的信号

您还可以在代码中进行信号连接而不是在编辑器中。当您通过代码实例化节点时,您并不能使用编辑器进行连接,因此这通常是必要的。

首先,在计时器的“节点”选项卡中选中’链接‘,并单击’断开连接‘来断开信号。

../../_images/signals_disconnect_timer.png

要在代码中进行连接,我们可以使用 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")

射击例子

作为信号使用的另一个例子,让我们考虑一个可以旋转并向鼠标射击的玩家角色。每次单击鼠标按钮,我们都会在玩家的位置创建子弹的实例。详情请参阅 实例化 。

然而,如果子弹作为玩家的子节点添加,那么当它旋转时,子弹将仍然保持“附着”在玩家身上:

../../_images/signals_shoot1.gif

相反,我们需要子弹独立于玩家的移动——一旦发射,子弹就会继续沿着直线运动,玩家就不能再影响它们了。与其作为玩家的孩子被添加到场景树中,不如将子弹作为“主”游戏场景的子节点添加上去更有意义,后者可能是玩家的父节点,甚至可能是更高层级的树。

你可以通过直接添加子弹来做到这一点:

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)

现在子弹将保持自己的运动独立于玩家的旋转:

../../_images/signals_shoot2.gif

总结

Godot的许多内置节点类型都提供可以用来检测事件的信号。例如,代表硬币的 Area2D 在玩家的物理体进入碰撞形状时发出 body_entered信号,让你知道玩家什么时候收集它。

在下一节 您的第一个游戏 中,您将构建一个完整的游戏,其中包括连接不同游戏组件的几种信号用法。

posted @ 2018-12-30 18:40  宸少凌  阅读(508)  评论(0编辑  收藏  举报

万年以来谁著史,三千里外欲封侯