Використання сигналів¶
На цьому уроці ми розглянемо сигнали. Це повідомлення, які видають вузли, коли з ними відбувається щось конкретне, наприклад, натискання кнопки. Інші вузли можуть підключатися до цього сигналу і викликати функцію, коли відбувається подія.
Сигнали є механізмом делегування, вбудований у Godot, що дозволяє одному ігровому об’єкту реагувати на зміни іншого, не посилаючись один на одного. Використання сигналів зменшує зв'язність і забезпечує гнучкість вашого коду.
Наприклад, на екрані може бути шкала життя, яка відображає здоров’я гравця. Коли гравець отримує пошкодження або використовує лікувальне зілля, ви хочете, щоб шкала відображала зміни. Для цього в Godot ви можете використовувати сигнали.
Примітка
Як згадувалося у вступі, сигнали є версією Godot шаблону спостерігача. Детальніше про шаблон спостерігач можна прочитати тут: https://gameprogrammingpatterns.com/observer.html
Тепер ми будемо використовувати сигнал, для керування рухом іконки Godot з попереднього уроку (Обробка вводу гравця).
Налаштування сцени¶
Щоб додати кнопку до нашої гри ми створимо нову сцену "main", яка буде містити і кнопку, і сцену Sprite.tscn
, створену в попередніх уроках.
Створіть нову сцену перейшовши до меню Сцена -> Нова Сцена.

На панелі Сцена клацніть кнопку 2D Сцена. Це додасть вузол Node2D в якості кореня.

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

Ми хочемо додати ще один вузол в якості, так би мовити, рідного брата Спрайта. Для цього клацніть правою кнопкою миші на Node2D і виберіть Додати дочірній вузол.

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

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

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

Натисніть і перетягніть саму кнопку, щоб наблизити її до спрайту.
Ви також можете вказати текст на кнопці, відредагувавши її властивість Text в Інспекторі. Введіть "Toggle motion".

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

Save your newly created scene. You can then run it with F6. At the moment, the button will be visible, but nothing will happen if you press it.
Підключення сигналів в редакторі¶
Тут ми хочемо підключити сигнал кнопки "pressed" до нашого спрайта, і ми хочемо викликати нову функцію, яка вмикатиме та вимикає її рух. Нам потрібно мати скрипт, приєднаний до вузла Sprite, що ми зробили в попередньому уроці.
Ви можете підключати сигнали на панелі Вузол. Виберіть вузол Кнопка і в правій частині редактора натисніть вкладку "Вузол" поруч із Інспектором.

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

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

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

The advanced view lets you connect to any node and any built-in function, add arguments to the callback, and set options. You can toggle the mode in the window's bottom-right by clicking the Advanced button.
Click the Connect button to complete the signal connection and jump to the Script workspace. You should see the new method with a connection icon in the left margin.

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

Давайте замінимо рядок із ключовим словом pass
на код, який перемикає рух вузла.
Наш Спрайт рухається завдяки коду в функції _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.gd
має виглядати так.
extends Sprite
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())
Запустіть сцену і натисніть кнопку, щоб побачити як спрайт стартує та зупиняється.
Підключення сигналів за допомогою коду¶
Ви можете підключати сигнали за допомогою коду замість того, щоб використовувати редактор. Це необхідно, коли ви створюєте вузли або вставляєте сцени всередину скрипту.
Давайте використаємо тут інший вузол. Godot має вузол таймера Timer, який корисний для реалізації часу зарядки навичок, перезавантаження зброї тощо.
Поверніться до 2D-робочого простору. Ви можете натиснути текст "2D" у верхній частині вікна або скористатися клавішами Ctrl + F1 (Alt + 1 на macOS).
На панелі Сцена клацніть правою кнопкою мишки на вузлі Sprite і додайте новий дочірній вузол. Знайдіть Timer і додайте відповідний вузол. Тепер ваша сцена повинна виглядати так.

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

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

Нам потрібно зробити дві операції для з'єднання вузлів за допомогою коду:
Отримайте посилання на Таймер зі Спрайту.
Викликати метод Таймера
connect()
.
Примітка
Щоб підключитися до сигналу за допомогою коду, потрібно викликати метод connect()
з вузла, який ви хочете прослухати. У цьому випадку ми хочемо прослухати сигнал Таймера "timeout".
Ми хочемо підключити сигнал, одразу після створення екземпляра сцени, і ми можемо зробити це за допомогою вбудованої функції Node._ready(), яка автоматично викликається рушієм, щойно вузол буде повністю створений.
Щоб отримати посилання на вузол відносно поточного, використовуємо метод Node.get_node(). Ми можемо зберігати посилання в змінній.
func _ready():
var timer = get_node("Timer")
Функція get_node()
переглядає нащадків Спрайта і отримує вузли за їх назвою. Наприклад, якщо ви перейменували вузол таймера з "Timer" на "BlinkingTimer" ("Блимаючий таймер") у редакторі, вам доведеться змінити виклик на get_node("BlinkingTimer")
.
Тепер ми можемо підключити Таймер до Спрайта у функції _ready()
.
func _ready():
var timer = get_node("Timer")
timer.connect("timeout", self, "_on_Timer_timeout")
Рядок можна прочитати так: підключаємо сигнал Таймера "timeout" до вузла, до якого приєднаний скрипт (self
). Коли таймер випромінює "timeout", ми хочемо викликати функцію "_on_Timer_timeout", яку нам потрібно визначити. Давайте додамо її в нижній частині нашого скрипту і використаємо для перемикання видимості нашого спрайта.
func _on_Timer_timeout():
visible = not visible
Властивість visible
контролює видимість нашого вузла. Рядок visible = not visible
перемикає значення. Якщо visible
є true
, то стає false
, і навпаки.
Якщо ви запустите сцену зараз, ви побачите, що спрайт блимає та вимикається з інтервалом в одну секунду.
Завершений скрипт¶
Ось і все для нашої маленької рухомої і миготливої іконки Godot! Ось повний файл Sprite.gd
для порівняння.
extends Sprite
var speed = 400
var angular_speed = PI
func _ready():
var timer = get_node("Timer")
timer.connect("timeout", self, "_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
Примітка
Оскільки сигнали представляють події, які щойно відбулися, ми зазвичай використовуємо дієслово дії в минулому часі в їх назвах.
Ваші сигнали працюють так само, як і вбудовані: вони з'являються на панелі Вузол, і ви можете підключитися до них, як і до будь-яких інших.

Щоб випромінювати сигнал у скриптах, використовуйте функцію emit_signal()
.
func take_damage(amount):
health -= amount
if health <= 0:
emit_signal("health_depleted")
Сигнал може при потребі оголошувати один, або кілька, аргументів. Вкажіть назви аргументів між дужками:
extends Node
signal health_changed(old_value, new_value)
Примітка
Аргументи сигналу відображаються у панелі вузла редактора, і Godot може використовувати їх для створення функцій зворотного виклику для вас. Однак ви все одно можете випромінювати будь-яку кількість аргументів, разом із сигналами. Ви повинні випромінювати правильні значення.
Щоб випромінювати значення разом із сигналом додайте їх як додаткові аргументи до функції emit_signal()
:
func take_damage(amount):
var old_health = health
health -= amount
emit_signal("health_changed", old_health, health)
Підсумок¶
Будь-який вузол в Godot випромінює сигнали, коли з ним відбувається щось конкретне, наприклад, натискається кнопка. Інші вузли можуть підключатися до окремих сигналів і реагувати на вибрані події.
Сигнали мають багато застосувань. З ними ви можете реагувати на вузол, що входить, чи виходить, з ігрового світу, на зіткнення, на персонажа, що входить, чи залишає область, на елемент інтерфейсу, що змінює розмір, і багато іншого.
Наприклад, Area2D, у вигляді монети, випромінює сигнал body_entered
кожного разу, коли стикається з фізичним тілом гравця, що дозволяє вам знати, що гравець її зібрав.
У наступному розділі, Ваша перша 2D гра, ви створите повну 2D гру і використаєте все, чого дізналися до цих пір, на практиці.