Сигналы

Введение

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

Примечание

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

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

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

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

To see how signals work, let’s try using a Timer node. Create a new scene with a Node2D and two children: a Timer and a Sprite. In the Scene dock, rename Node2D to TimerExample.

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

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

../../_images/signals_node_setup.png

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

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

../../_images/signals_node_tab_timer.png

Click on the «timeout()» signal and click «Connect…» at the bottom of the signals panel. You’ll see the following window, where you can define how you want to connect the signal:

../../_images/signals_connect_dialog_timer.png

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

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

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

On the bottom of the window is a field labeled «Receiver Method». This is the name of the function in the target node’s script that you want to use. By default, Godot will create this function using the naming convention _on_<node_name>_<signal_name> but you can change it if you wish.

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

extends Node2D

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

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

extends Node2D

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 : Node2D
{
    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 Node2D

func _ready():
    $Timer.connect("timeout", self, "_on_Timer_timeout")

func _on_Timer_timeout():
    $Sprite.visible = !$Sprite.visible
public class TimerExample : Node2D
{
    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 Node2D

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

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

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

extends Node2D

signal my_signal

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

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

Вывод

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

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