Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Використання сигналів

На цьому уроці ми розглянемо сигнали. Це повідомлення, які видають вузли, коли з ними відбувається щось конкретне, наприклад, натискання кнопки. Інші вузли можуть підключатися до цього сигналу і викликати функцію, коли відбувається подія.

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

Наприклад, на екрані може бути шкала життя, яка відображає здоров’я гравця. Коли гравець отримує пошкодження або використовує лікувальне зілля, ви хочете, щоб шкала відображала зміни. В Godot для цього ви можете використовувати сигнали.

Як і методи (Callable), сигнали є першокласним типом з Godot 4.0. Це означає, що ви можете передавати їх як аргументи методу безпосередньо без необхідності передавати їх як рядки, що забезпечує краще автозавершення та менш схильне до помилок. Перегляньте довідник класу class_signal, щоб отримати список того, що ви можете робити безпосередньо з типом Signal.

Дивись також

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

Тепер ми будемо використовувати сигнал, для керування рухом іконки Godot з попереднього уроку (Обробка вводу гравця).

Примітка

У цьому проєкті ми будемо дотримуватися правил іменування Godot.

  • GDScript: Класи (вузли) використовують PascalCase, змінні та функції використовують snake_case, а константи використовують ALL_CAPS (Дивіться Посібник зі стилю GDScript ).

  • C#: Класи, експортовані змінні та методи використовують PascalCase, приватні поля використовують _camelCase, локальні змінні та параметри використовують camelCase (Дивіться Настанови по стилю C# ). Будьте обережні, вводячи назви методів саме під час підключення сигналів.

Налаштування сцени

Щоб додати кнопку до нашої гри, ми створимо нову сцену, яка включатиме як Button, так і сцену sprite_2d.tscn, яку ми створили в уроці Створення першого сценарію.

Створіть нову сцену, перейшовши до меню Scene > New Scene.

../../_images/signals_01_new_scene.webp

У доці «Сцена» натисніть кнопку 2D Scene. Це додасть Node2D як наш корінь.

../../_images/signals_02_2d_scene.webp

На панелі Файлова система клацніть і перетягніть файл sprite_2d.tscn, який ви зберегли раніше, на Node2D, щоб створити його екземпляр.

../../_images/signals_03_dragging_scene.webp

Ми хочемо додати ще один вузол як брата/сестрину Sprite2D. Для цього клацніть правою кнопкою миші на Node2D та виберіть Add Child Node.

../../_images/signals_04_add_child_node.webp

Знайдіть вузол Button та додайте його.

../../_images/signals_05_add_button.webp

Вузол за замовчуванням невеликий. Натисніть і перетягніть нижній правий маркер Кнопки у вікні перегляду, щоб змінити її розмір.

../../_images/signals_06_drag_button.png

Якщо ви не бачите маркерів, переконайтеся, що інструмент вибору активний на панелі інструментів.

../../_images/signals_07_select_tool.webp

Натисніть і перетягніть саму кнопку, щоб наблизити її до спрайту.

Ви також можете написати підпис для кнопки, відредагувавши її властивість Text в Inspector. Введіть Toggle motion.

../../_images/signals_08_toggle_motion_text.webp

Ваше дерево сцени та область перегляду мають виглядати так.

../../_images/signals_09_scene_setup.webp

Збережіть вашу новостворену сцену як node_2d.tscn, якщо ви цього ще не зробили. Ви можете її запустити натиснувши F6 (Cmd + R на macOS). В даний момент кнопку буде видно, проте при натисканні на неї, нічого не буде відбуватися.

Підключення сигналів в редакторі

Тут ми хочемо підключити сигнал кнопки "pressed" до нашого Sprite2D, і ми хочемо викликати нову функцію, яка вмикатиме та вимикає його рух. Нам потрібно мати скрипт, приєднаний до вузла Sprite2D, що ми зробили в попередньому уроці.

Ви можете підключати сигнали в доці Signals. Виберіть вузол Button і, у правій частині редактора, натисніть на вкладку з назвою Signals поруч з Inspector.

../../_images/signals_10_node_dock.webp

Панель відображає список сигналів, доступних на вибраному вузлі.

../../_images/signals_11_pressed_signals.webp

Двічі клацніть сигнал "pressed", щоб відкрити вікно підключення вузла.

../../_images/signals_12_node_connection.webp

Там ви можете підключити сигнал до вузла Sprite2D. Вузлу потрібен метод приймача, функція, яку Godot буде викликати, коли Кнопка випромінює сигнал. Редактор створить її для вас. За умовою ми називаємо ці методи зворотного виклику "_on_НазваВузла_назва_сигналу". Тут це буде "_on_Button_pressed".

Примітка

When connecting signals via the editor's Signals dock, you can use two modes. The simple one only allows you to connect to nodes that have a script attached to them and creates a new callback function on them.

../../_images/signals_advanced_connection_window.webp

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

Примітка

Якщо ви використовуєте зовнішній редактор (наприклад, VS Code), ця автоматична генерація коду може не працювати. У цьому випадку вам потрібно підключити сигнал за допомогою коду, як описано в наступному розділі.

Натисніть кнопку Connect, щоб завершити підключення сигналу та перейти до робочої області Script. Ви повинні побачити новий метод із значком підключення у лівому полі.

../../_images/signals_13_signals_connection_icon.webp

Якщо натиснути іконку, з’явиться вікно з інформацією про з’єднання. Ця функція доступна лише при підключенні вузлів у редакторі.

../../_images/signals_14_signals_connection_info.webp

Давайте замінимо рядок із ключовим словом pass на код, який перемикає рух вузла.

Наш Sprite2D рухається завдяки коду в функції _process(). Godot надає метод вмикання та вимикання обробки: Node.set_process(). Інший метод класу Node , is_processing(), повертає true, якщо обробка активна. Ми можемо використовувати ключове слово not для інвертування значення.

func _on_button_pressed():
    set_process(not is_processing())

Ця функція перемикає обробку і, в свою чергу, рух значка вмикається і вимикається при натисканні кнопки.

Перш ніж спробувати гру нам потрібно спростити нашу функцію _process() до самостійного переміщення вузла без очікування на введення гравця. Замініть її наступним кодом, який ми бачили два уроки тому:

func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta

Ваш повний код sprite_2d.gd має виглядати так.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())

Запустіть поточну сцену, натиснувши F6 (Cmd + R на macOS), і натисніть кнопку, щоб побачити запуск і зупинку спрайта.

Підключення сигналів за допомогою коду

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

Давайте використаємо тут інший вузол. Godot має вузол таймера Timer, який корисний для реалізації часу зарядки навичок, перезавантаження зброї тощо.

Поверніться до 2D-робочого простору. Ви можете натиснути текст "2D" у верхній частині вікна або скористатися клавішами Ctrl + F1 (Ctrl + Cmd + 1 на macOS).

На панелі Сцена клацніть правою кнопкою мишки на вузлі Sprite2D і додайте новий дочірній вузол. Знайдіть Timer і додайте відповідний вузол. Тепер ваша сцена повинна виглядати так.

../../_images/signals_15_scene_tree.webp

Вибравши вузол Timer, перейдіть до Inspector та увімкніть властивість Autostart.

../../_images/signals_18_timer_autostart.webp

Клацніть піктограму скрипта поруч із Sprite2D, щоб повернутися до робочої області скриптів.

../../_images/signals_16_click_script.webp

Нам потрібно зробити дві операції для з'єднання вузлів за допомогою коду:

  1. Отримайте посилання на Таймер зі Sprite2D.

  2. Викликати метод connect() на сигнал Таймера "timeout".

Примітка

Щоб підключитися до сигналу за допомогою коду, потрібно викликати метод connect() сигналу, який ви хочете прослухати. У цьому випадку ми хочемо прослухати сигнал Таймера "timeout".

Ми хочемо підключити сигнал, одразу після створення екземпляра сцени, і ми можемо зробити це за допомогою вбудованої функції Node._ready(), яка автоматично викликається рушієм, щойно вузол буде повністю створений.

Щоб отримати посилання на вузол відносно поточного, використовуємо метод Node.get_node(). Ми можемо зберігати посилання в змінній.

func _ready():
    var timer = get_node("Timer")

Функція get_node() переглядає нащадків Sprite2D і отримує вузли за їх назвою. Наприклад, якщо ви перейменували вузол таймера з "Timer" на "BlinkingTimer" у редакторі, вам доведеться змінити виклик на get_node("BlinkingTimer").

Тепер ми можемо підключити Таймер до Sprite2D у функції _ready().

func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)

Рядок можна прочитати так: підключаємо сигнал Таймера "timeout" до вузла, до якого приєднаний скрипт. Коли таймер випромінює timeout, ми хочемо викликати функцію _on_timer_timeout(), яку нам потрібно визначити. Давайте додамо її в нижній частині нашого скрипту і використаємо для перемикання видимості нашого спрайта.

Примітка

За домовленістю, ми називаємо ці методи зворотного виклику по конструкції "_on_назва_вузла_назва_сигналу" в GDScript і "OnНазваВузлаНазваСигналу" в C#. Тут це буде "_on_timer_timeout" для GDScript і OnTimerTimeout() для C#.

func _on_timer_timeout():
    visible = not visible

Властивість visible контролює видимість нашого вузла. Рядок visible = not visible перемикає значення. Якщо visible є true, то стає false, і навпаки.

Якщо ви зараз запустите сцену Node2D, ви побачите, що спрайт блимає та вимикається з інтервалом в одну секунду.

Завершений скрипт

Ось і все для нашої маленької рухомої і миготливої іконки Godot! Ось повний файл sprite_2d.gd для порівняння.

extends Sprite2D

var speed = 400
var angular_speed = PI


func _ready():
    var timer = get_node("Timer")
    timer.timeout.connect(_on_timer_timeout)


func _process(delta):
    rotation += angular_speed * delta
    var velocity = Vector2.UP.rotated(rotation) * speed
    position += velocity * delta


func _on_button_pressed():
    set_process(not is_processing())


func _on_timer_timeout():
    visible = not visible

Власні сигнали

Примітка

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

Ви можете визначити власні сигнали в скрипті. Скажімо, ви хочете показати екран завершення гри, коли здоров'я гравця досягає нуля. Для цього ви можете визначити сигнал під назвою "died" або "health_depleted", коли їх здоров'я досягає 0.

extends Node2D

signal health_depleted

var health = 10

Примітка

Оскільки сигнали представляють події, які щойно відбулися, ми зазвичай використовуємо дієслово дії в минулому часі в їх назвах.

Ваші сигнали працюють так само, як і вбудовані: вони відображаються на вкладці Signals, і ви можете підключитися до них, як до будь-яких інших.

../../_images/signals_17_custom_signal.webp

Щоб випромінювати сигнал у ваших скриптах, викликайте на сигналі emit().

func take_damage(amount):
    health -= amount
    if health <= 0:
        health_depleted.emit()

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

extends Node2D

signal health_changed(old_value, new_value)

var health = 10

Примітка

The signal arguments show up in the editor's Signals 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.

Щоб випромінювати значення разом із сигналом додайте їх як додаткові аргументи до функції emit():

func take_damage(amount):
    var old_health = health
    health -= amount
    health_changed.emit(old_health, health)

Підсумок

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

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

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

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