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())

В основной сцене мы затем подключаем сигнал проигрывателя (он появится на вкладке "Node" в Инспекторе)

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