Сигнали

Вступ

Сигнали в 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.

Нижче ви можете побачити приклади того, як можна використовувати сигнали у власних проектах.

Timer example

Щоб побачити, як працюють сигнали, спробуємо використати вузол Timer (Таймер). Створіть нову сцену з вузлом Node2D та двома нащадками: Timer та Sprite. На панелі Сцена перейменуйте Node2D на TimerExample (Зразок Таймера).

Для текстури Спрайта можна використовувати значок Godot, або будь-яке інше зображення, яке вам подобається. Зробіть це, вибравши Завантажити у спадному меню атрибута Спрайта Texture. Приєднайте скрипт до кореневого вузла, але не додавайте до нього коду.

Ваше дерево сцени має виглядати так:

../../_images/signals_node_setup.png

У властивостях вузла Таймер встановіть прапорець «Увімкнено» біля пункту Autostart (Автозапуск). Це призведе до автоматичного запуску таймера під час запуску сцени. Ви можете залишити Wait Time (час очікування) 1 секунду.

Поруч із вкладкою «Інспектор» знаходиться вкладка з написом «Вузол». Перейдіть на цю вкладку, і ви побачите всі сигнали, які може видавати вибраний вузол. У випадку з вузлом Timer, ми маємо справу з «timeout()». Цей сигнал випромінюється щоразу, коли таймер досягає 0.

../../_images/signals_node_tab_timer.png

Клацніть на сигнал «timeout ()» і натисніть «Приєднати …» внизу панелі сигналів. Ви побачите наступне вікно, де можна визначити, як ви хочете підключити сигнал:

../../_images/signals_connect_dialog_timer.png

Ліворуч ви побачите вузли у вашій сцені і з них ви можете вибрати вузол для «прослуховування» сигналу. Зауважте, що вузол Timer синій, це візуальна ознака того, що саме цей вузол випромінює сигнал. Виберіть кореневий вузол.

Попередження

До вибраного вузла повинен бути доданий скрипт, інакше ви отримаєте повідомлення про помилку.

On the right side, you can bind an arbitrary number of arguments of (possibly) different types. This can be useful when you have more than one signal connected to the same method, as each signal propagation will result in different values for those extra call arguments.

У нижній частині вікна розташоване поле з написом «Метод-отримувач». Це ім’я функції в скрипті цільового вузла, яку ви хочете використовувати. За замовчуванням Godot створить цю функцію за допомогою конвенції про іменування _on_<node_name>_<signal_name> (_on_<ім'я_вузла>_<ім'я_сигнала>) але ви можете змінити її, якщо бажаєте.

Натисніть «З’єднати», і ви побачите, що функція була створена в скрипті:

extends Node2D

func _on_Timer_timeout():
    pass # replace with function body
public class TimerExample : Node2D
{
    public 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() так, що з’єднання буде здійснено при запуску. Синтаксис функції <сигнальний_вузол>.connect(<ім'я_сигналу>, <цільовий_вузол>, <ім'я_цільової_функції>). Ось код для підключення нашого таймера:

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));
    }
}

A signal can also optionally declare one or more arguments. Specify the argument names between parentheses:

extends Node

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

Примітка

The signal arguments show up in the editor’s node dock, and Godot can use them to generate callback functions for you. However, you can still emit any number of arguments when you emit signals. So it’s up to you to emit the correct values.

To pass values, add them as the second argument to the emit_signal function:

extends Node

signal my_signal(value, other_value)

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

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

Висновки

Багато вбудованих типів вузлів Godot надають сигнали, які можна використовувати для виявлення подій. Наприклад, Area2D, у вигляді монети, випромінює сигнал body_entered кожного разу, коли стикається з фізичним тілом гравця, що дозволяє вам знати, що гравець її зібрав.

У наступному розділі, Ваша перша гра, ви побудуєте повну гру, з використанням кількох різних сигналів для підключення різних ігрових компонентів.