Сигналы

Введение

Сигналы в Godot являются реализацией программного шаблона наблюдатель. Они позволяют узлу отправлять сообщения, которые другие узлы могут прослушивать и на которые могут отвечать. Например, вместо того, чтобы постоянно проверять, не нажата ли кнопка, можно просто выдать сигнал при нажатии.

Примечание

Вы можете прочитать больше о шаблоне Наблюдатель здесь: http://gameprogrammingpatterns.com/observer.html

Сигналы - это способ уменьшить зацепление игровых объектов, приводящий код к более упорядоченному и управляемому виду. Вместо того чтобы принуждать игровые объекты постоянно зависеть от существования других объектов, возможно выдавать сигналы, на которые заинтересованные объекты могут реагировать.

Ниже вы можете увидеть некоторые примеры того, как вы можете использовать сигналы в ваших собственных проектах.

Пример таймера

Чтобы увидеть, как работают сигналы, давайте попробуем использовать узел Timer. Вы можете использовать иконку Godot для текстуры спрайта или любое другое изображение, которое вам нравится. Присоедините скрипт к корневому узлу, но пока не добавляйте в него никакого кода.

Для текстуры Спрайта вы можете использовать иконку Godot, или любое другое изображение. Сделайте это выбрав Загрузить в аттрибуте Sprite Texture в выпадающем меню. Прикрепите скрипт к корневому узлу, но пока не добавляйте код.

Дерево сцены должно выглядеть так:

../../_images/signals_node_setup.png

В свойствах узла Timer установите флажок «On» рядом с полем Autostart. Это приведет к автоматическому запуску таймера при запуске сцены. Вы можете оставить Wait Time в 1 секунду.

Рядом с вкладкой «Инспектор» находится вкладка «Узел». Нажмите на эту вкладку, и вы увидите все сигналы, которые выбранный узел может излучать. В случае с узлом Timer, с которым мы связаны, это «timeout». Этот сигнал генерируется каждый раз, когда таймер достигает 0.

../../_images/signals_node_tab_timer.png

Нажмите на сигнал «timeout()» и после на кнопку «Присоединить…». Вы увидите окно, где вы можете определить, как вы хотите подключить сигнал:

../../_images/signals_connect_dialog_timer.png

На левой стороне, вы увидите узлы в вашей сцене и можете выбрать узел, который вы хотите использовать для «прослушки» сигнала. Обратите внимание, что узел Timer является красным - это не ошибка, а визуальный признак того, что это узел, который излучает сигнал. Выберите корневой узел.

Предупреждение

На выбранный узел должен быть прикреплен скрипт, иначе вы получите сообщение об ошибке.

В нижней части окна находится поле с надписью «Method In Node». Это имя функции в скрипте данного узла, который требуется использовать. По умолчанию Godot создаст эту функцию, используя соглашение об именовании _on_<node_name>_<signal_name>, но вы можете изменить его, если хотите.

Нажмите кнопку «Присоединить», и вы увидите, что функция была создана в скрипте:

extends Node

func _on_Timer_timeout():
    pass # replace with function body
public class TimerExample : Node
{
    private void _on_Timer_timeout()
    {
        // Replace with function body.
    }
}

Теперь мы можем заменить изначальный код на любой код, который мы хотим запустить при получении сигнала. Давайте заставим спрайт мигать:

extends Node

func _on_Timer_timeout():
    # Note: the `$` operator is a shorthand for `get_node()`,
    # so `$Sprite` is equivalent to `get_node("Sprite")`.
    $Sprite.visible = !$Sprite.visible
public class TimerExample : Node
{
    public void _on_Timer_timeout()
    {
        var sprite = GetNode<Sprite>("Sprite");
        sprite.Visible = !sprite.Visible;
    }
}

Запустите сцену, и вы увидите что спрайт мигает и пропадает каждую секунду. Можно изменить свойство таймера Wait Time, чтобы изменить частоту мигания.

Подключение сигналов в коде

Вы также можете присоединить сигнал в коде, а не в редакторе. Это обычно необходимо при создании экземпляров узлов с помощью кода и поэтому невозможно использовать редактор для подключения.

Во-первых, отключите сигнал, выбрав подключенный сигнал таймера на вкладке «Узел» и нажав Отсоединить.

../../_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
public class TimerExample : Node
{
    public override void _Ready()
    {
        GetNode("Timer").Connect("timeout", this, nameof(_on_Timer_timeout));
    }

    public void _on_Timer_timeout()
    {
        var sprite = GetNode<Sprite>("Sprite");
        sprite.Visible = !sprite.Visible;
    }
}

Пользовательские сигналы

Вы также можете объявить свои собственные пользовательские сигналы в Godot:

extends Node

signal my_signal
public class Main : Node
{
    [Signal]
    public delegate void MySignal();
}

После объявления ваши пользовательские сигналы будут появляться в инспекторе и могут быть соединены так же, как и встроенные сигналы узла.

Чтобы выпустить сигнал через код, используйте функцию emit_signal:

extends Node

signal my_signal

func _ready():
    emit_signal("my_signal")
public class Main : Node
{
    [Signal]
    public delegate void MySignal();

    public override void _Ready()
    {
        EmitSignal(nameof(MySignal));
    }
}

Вывод

Многие встроенные типы узлов в Godot предоставляют сигналы, которые можно использовать для обнаружения событий. Например, монетка, созданная в виде объекта Area2D, излучает сигнал body_entered в момент касания физическим телом игрока формы столкновения монетки, позволяя понять, когда игрок её собрал.

В следующем разделе Ваша Первая Игра, вы будете делать готовую игру, с использованием некоторых видов сигналов для подключения различных игровых компонентов.