Сигналы

Введение

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

Примечание

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

Signals are a way to decouple your game objects, which leads to better organized and more manageable code. Instead of forcing game objects to expect other objects to always be present, they can instead emit signals that all interested objects can subscribe to and respond to.

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

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

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

For the Sprite’s texture, you can use the Godot icon, or any other image you like. Do so by selecting Load in the Sprite’s Texture attribute drop-down menu. Attach a script to the root node, but don’t add any code to it yet.

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

../../_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();
}

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

To emit a signal via code, use the emit_signal function:

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 в момент касания физическим телом игрока формы столкновения монетки, позволяя понять, когда игрок её собрал.

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