Создание экземпляра класса с помощью сигналов

Сигналы обеспечивают способ развязки игровых объектов, позволяя вам избежать принудительного фиксированного расположения узлов. Одним из признаков того, что сигнал может быть востребован, является использование get_parent(). Прямая ссылка на родителя узла означает, что вы не можете легко переместить этот узел в другое место в дереве сцены. Это может быть особенно проблематично, если вы создаете объекты во время выполнения и хотите поместить их в произвольное место в дереве сцены.

Ниже мы рассмотрим пример такой ситуации: стрельба пулями.

Пример стрельбы

Рассмотрим персонажа игрока, который может вращаться и стрелять в направлении мыши. Каждый раз при нажатии кнопки мыши мы создаём экземпляр пули в позиции игрока. Подробнее см. Создание экземпляров.

Мы используем 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