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)

ただし、これは別の問題につながります。 「プレイヤー」シーンを個別にテストしようとすると、(弾丸の追加先として)アクセスする親ノードがないため、確認時にクラッシュします。これにより、プレイヤーコードを個別にテストすることが非常に難しくなります。また、メインシーンのノード構造を変更する場合、プレイヤーの親が弾丸を受け取る適切なノードではなくなる可能性があります。

これに対する解決策は、プレイヤーから弾丸を「放出」するシグナルを使用することです。プレイヤーはその後、弾丸に何が起こるかを「知る」必要はありません - シグナルに接続されているノードは、弾丸を「受信」し、弾丸を発生させるために適切なアクションを実行できます。

シグナルを使用して弾丸を発射するプレイヤーのコードは次のとおりです:

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