Сигнали

Вступ

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

Примітка

Детальніше про шаблон спостерігач можна прочитати тут: http://gameprogrammingpatterns.com/observer.html

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

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

Приклад таймера

Щоб побачити, як працюють сигнали, спробуємо використати вузол 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 синій, це візуальна ознака того, що саме цей вузол випромінює сигнал. Виберіть кореневий вузол.

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

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

If you toggle the Advanced menu, you’ll see on the right side that 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));
    }
}

Сигнал може також (необов’язково) оголошувати один, або кілька, аргументів. Вкажіть назви аргументів між дужками:

extends Node


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

Примітка

Аргументи сигналу відображаються у панелі вузла редактора, і Godot може використовувати їх для створення функцій зворотного виклику для вас. Однак ви все одно можете випромінювати будь-яку кількість аргументів, разом із сигналами. Ви повинні випромінювати правильні значення.

Щоб випромінювати сигнал за допомогою коду, використовуйте функцію emit_signal:

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(bool value, int other_value);

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

Висновки

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

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