Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Instantiation with signals

Signals provide a way to decouple game objects, allowing you to avoid forcing a fixed arrangement of nodes. One sign that a signal might be called for is when you find yourself using get_parent(). Referring directly to a node's parent means that you can't easily move that node to another location in the scene tree. This can be especially problematic when you are instantiating objects at runtime and may want to place them in an arbitrary location in the running scene tree.

下面我們將舉一個這種情境的例子:發射子彈。

射擊範例

假設有一個玩家角色可以旋轉並朝滑鼠方向射擊。每次點擊滑鼠按鈕時,我們就會在玩家的位置建立一顆子彈的實例。詳情請參考 編輯實體

我們將用 Area2D 節點作為子彈,它會以指定的速度直線移動:

extends Area2D

var velocity = Vector2.RIGHT

func _physics_process(delta):
    position += velocity * delta

不過,如果子彈被加為玩家的子節點,當角色旋轉時,子彈仍會「跟著」玩家移動:

../../_images/signals_shoot1.gif

我們其實需要子彈獨立於玩家的移動——一旦射出,子彈就應該自行沿直線飛行,且玩家再也無法影響它們。與其把子彈加到玩家底下,不如加到「主要」遊戲場景(可能是玩家的父節點或更上層)作為子節點更合適。

你可以直接把子彈加到主場景來達到這個效果:

var bullet_instance = Bullet.instantiate()
get_parent().add_child(bullet_instance)

但這又會帶來另一個問題。如果你想獨立測試「Player」場景,射擊時就會當機,因為找不到父節點。這讓獨立測試玩家程式碼變得困難,而且如果你未來更動主場景的節點結構,玩家的父節點也可能就不再適合作為接收子彈的那個節點。

解決方法是使用訊號,讓玩家「發射」子彈。這樣玩家就不需要「知道」子彈後續發生了什麼——只要有節點連接到這個訊號,就能「接收」子彈並負責產生它們。

以下是玩家角色用訊號發射子彈的程式碼:

extends Sprite2D

signal shoot(bullet, direction, location)

var Bullet = preload("res://bullet.tscn")

func _input(event):
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
            shoot.emit(Bullet, rotation, position)

func _process(delta):
    look_at(get_global_mouse_position())

在主場景中,我們要將玩家的訊號連接起來(會出現在「節點」分頁的檢查器裡)

func _on_player_shoot(Bullet, direction, location):
    var spawned_bullet = Bullet.instantiate()
    add_child(spawned_bullet)
    spawned_bullet.rotation = direction
    spawned_bullet.position = location
    spawned_bullet.velocity = spawned_bullet.velocity.rotated(direction)

現在子彈將獨立於玩家旋轉,自行運動:

../../_images/signals_shoot2.gif