Написання скриптів (продовження)¶
Обробка¶
Кілька дій у Godot викликаються зворотними викликами, або віртуальними функціями, тому не потрібно писати код, який працює весь час.
Однак, як правило, потрібен скрипт для обробки на кожному кадрі. Існує два типи обробки: обробка в режимі очікування та обробка фізики.
Обробка в режимі очікування активується, коли в скрипті знайдено метод Node._process(). Його можна вимкнути та увімкнути за допомогою функції Node.set_process().
Цей метод викликатиметься кожного разу, коли малюється кадр:
func _process(delta):
# Do something...
pass
public override void _Process(float delta)
{
// Do something...
}
Важливо враховувати, що частота, з якою функція _process()
буде викликатися, залежить від того, скільки кадрів в секунду (FPS) працює ваша програма. Цей показник може змінюватись в залежності від часу та пристроїв.
Для управління цією властивістю є параметр delta
, який містить час у секундах, що минув від попереднього виклику _process()
.
Цей параметр може бути використаний для того, щоб дії завжди займали однаковий час, незалежно від FPS гри.
Наприклад, рух часто множать на дельту часу, щоб зробити швидкість руху постійною і незалежною від частоти кадрів.
Обробка фізики з _physics_process()
є аналогічною, але її слід використовувати для процесів, які повинні відбуватися перед кожним етапом фізики, наприклад, керування персонажем. Вона завжди працює перед початком фізичного кроку і викликається через фіксований інтервал часу: 60 разів за секунду за замовчуванням. Інтервал можна змінити в Параметрах проєкту, у розділі Фізика -> Загальні -> Фізика Fps.
Функція _process()
, однак, не синхронізована з фізикою. Частота кадрів не є постійною і залежить від апаратної та ігрової оптимізації. В одно-поточних іграх її виконання відбувається після кроку фізики .
Простий спосіб побачити функцію _process()
в роботі - це створити сцену з одного вузла Label із наступним скриптом:
extends Label
var accum = 0
func _process(delta):
accum += delta
text = str(accum) # 'text' is a built-in label property.
public class CustomLabel : Label
{
private float _accum;
public override void _Process(float delta)
{
_accum += delta;
Text = _accum.ToString(); // 'Text' is a built-in label property.
}
}
Який покаже лічильник, що збільшується кожен кадр.
Групи¶
Групи в Godot працюють як теги, які, можливо, ви зустрічали в інших програмах. Вузол можна додати до бажаної кількості груп. Це корисна функція для організації великих сцен. Є два способи додавання вузлів до груп. Перший - з користувацького інтерфейсу, використовуючи кнопку "Групи" на панелі "Вузол":

А другий шлях - з коду. Наступний скрипт додав би поточний вузол до групи enemies
, як тільки вона з'явиться у дереві сцени.
func _ready():
add_to_group("enemies")
public override void _Ready()
{
base._Ready();
AddToGroup("enemies");
}
Таким чином, якщо гравця, який пробирається в таємну базу, виявлено, то всі вороги можуть дізнатися про це завдяки піднятій тривозі за допомогою SceneTree.call_group():
func _on_discovered(): # This is a purely illustrative function.
get_tree().call_group("enemies", "player_was_discovered")
public void _OnDiscovered() // This is a purely illustrative function.
{
GetTree().CallGroup("enemies", "player_was_discovered");
}
Вищевказаний код викликає функцію player_was_discovered
для кожного члена групи enemies
.
Також можна отримати повний список вузлів enemies
, викликавши SceneTree.get_nodes_in_group():
var enemies = get_tree().get_nodes_in_group("enemies")
var enemies = GetTree().GetNodesInGroup("enemies");
Клас SceneTree пропонує безліч корисних функцій, таких як взаємодія зі сценами, ієрархією їх вузлів та групами вузлів. Він дозволяє вам легко перемикати сцени, або перезавантажувати їх, виходити з гри, або призупиняти та відновлювати її. Він навіть надходить із цікавими сигналами. Тож поекспериментуйте, якщо маєте трохи часу!
Сповіщення¶
У Godot існує система сповіщень. Зазвичай вони не потрібні для скриптів, оскільки це занадто низький рівень і для більшості з них передбачені віртуальні функції. Просто добре знати, що вони існують. Наприклад, ви можете додати функцію Object._notification() у свій скрипт:
func _notification(what):
match what:
NOTIFICATION_READY:
print("This is the same as overriding _ready()...")
NOTIFICATION_PROCESS:
print("This is the same as overriding _process()...")
public override void _Notification(int what)
{
base._Notification(what);
switch (what)
{
case NotificationReady:
GD.Print("This is the same as overriding _Ready()...");
break;
case NotificationProcess:
var delta = GetProcessDeltaTime();
GD.Print("This is the same as overriding _Process()...");
break;
}
}
Документація кожного класу у Class Reference показує сповіщення, які він може отримувати. Однак у більшості випадків GDScript надає більш прості функції для перезапису.
Переобумовленої функції¶
Функції, які описані нижче, можуть бути застосовані до вузлів:
func _enter_tree():
# When the node enters the Scene Tree, it becomes active
# and this function is called. Children nodes have not entered
# the active scene yet. In general, it's better to use _ready()
# for most cases.
pass
func _ready():
# This function is called after _enter_tree, but it ensures
# that all children nodes have also entered the Scene Tree,
# and became active.
pass
func _exit_tree():
# When the node exits the Scene Tree, this function is called.
# Children nodes have all exited the Scene Tree at this point
# and all became inactive.
pass
func _process(delta):
# This function is called every frame.
pass
func _physics_process(delta):
# This is called every physics frame.
pass
public override void _EnterTree()
{
// When the node enters the Scene Tree, it becomes active
// and this function is called. Children nodes have not entered
// the active scene yet. In general, it's better to use _ready()
// for most cases.
base._EnterTree();
}
public override void _Ready()
{
// This function is called after _enter_tree, but it ensures
// that all children nodes have also entered the Scene Tree,
// and became active.
base._Ready();
}
public override void _ExitTree()
{
// When the node exits the Scene Tree, this function is called.
// Children nodes have all exited the Scene Tree at this point
// and all became inactive.
base._ExitTree();
}
public override void _Process(float delta)
{
// This function is called every frame.
base._Process(delta);
}
public override void _PhysicsProcess(float delta)
{
// This is called every physics frame.
base._PhysicsProcess(delta);
}
Як було сказано раніше, краще використовувати ці функції замість системи сповіщень.
Створення вузлів¶
Щоб створити вузол з коду, викличте метод .new()
, як і для будь-якого іншого типу даних на основі класу. Наприклад:
var s
func _ready():
s = Sprite.new() # Create a new sprite!
add_child(s) # Add it as a child of this node.
private Sprite _sprite;
public override void _Ready()
{
base._Ready();
_sprite = new Sprite(); // Create a new sprite!
AddChild(_sprite); // Add it as a child of this node.
}
Щоб видалити вузол, чи то всередині, чи поза сценою, необхідно використовувати free()
:
func _someaction():
s.free() # Immediately removes the node from the scene and frees it.
public void _SomeAction()
{
_sprite.Free(); // Immediately removes the node from the scene and frees it.
}
Коли вузол видаляється, разом з ним видаляються і всі його вузли нащадки. Через це видалення вузлів вручну набагато простіше, ніж здається. Видаляєте базовий вузол, і всі його нащадки видаляться з ним.
Може виникнути ситуація, коли ми хочемо видалити вузол, який наразі "заблокований", оскільки він випромінює сигнал, або викликає функцію. Це призведе до краху гри. Запуск Godot з відладчиком часто вловлює такий випадок і попереджатиме про це.
Найбезпечніший спосіб видалити вузол - використати Node.queue_free(). Він безпечно стирає вузол під час роботи в режимі очікування.
func _someaction():
s.queue_free() # Removes the node from the scene and frees it when it becomes safe to do so.
public void _SomeAction()
{
_sprite.QueueFree(); // Removes the node from the scene and frees it when it becomes safe to do so.
}
Інстансування сцени¶
Інстансування сцени з коду робиться в два етапи. Перший - це завантаження сцени з вашого жорсткого диска:
var scene = load("res://myscene.tscn") # Will load when the script is instanced.
var scene = GD.Load<PackedScene>("res://myscene.tscn"); // Will load when the script is instanced.
Попереднє завантаження може бути зручнішим, так як відбувається під час розбору (parse time) (лише для GDScript):
var scene = preload("res://myscene.tscn") # Will load when parsing the script.
Але scene
це ще не вузол. Він упакований у спеціальний ресурс під назвою PackedScene. Щоб створити справжній вузол, необхідно викликати функцію PackedScene.instance(). Це поверне дерево вузлів, які можна додати до активної сцени:
var node = scene.instance()
add_child(node)
var node = scene.Instance();
AddChild(node);
Перевага цього двохетапного процесу полягає в тому, що упакована сцена може зберігатися завантаженою і готова до використання, так що ви можете створити стільки екземплярів, скільки бажаєте. Це особливо корисно для швидкого встановлення кількох ворогів, куль та інших об'єктів на активній сцені.
Реєстрація скриптів в якості класів¶
У Godot є функція "Script Class" для реєстрації окремих скриптів у Редакторі. За замовчуванням ви можете отримати доступ лише до безіменних скриптів, завантаживши файл безпосередньо.
Ви можете назвати скрипт і зареєструвати його як тип у редакторі з class_name
ключовим словом, за яким вказується ім'я класу. Ви можете додати кому та необов'язковий шлях до зображення, яке використовуватиметься як значок. Потім ви знайдете новий тип у діалоговому вікні створення вузла або ресурсу.
extends Node
# Declare the class name here
class_name ScriptName, "res://path/to/optional/icon.svg"
func _ready():
var this = ScriptName # reference to the script
var cppNode = MyCppNode.new() # new instance of a class named MyCppNode
cppNode.queue_free()

Попередження
В Godot 3.1:
Лише GDScript і NativeScript, тобто C ++ та інші мови, що працюють на GDNative, можуть реєструвати скрипти.
Тільки GDScript створює глобальні змінні для кожного названого скрипта.