RefCounted и Node
Часть раздела Объектно-ориентированное программирование путешествия по GDScript на Coddy. Урок 43 из 65.
Каждый объект занимает память, пока его не освободят. Объекты RefCounted и Node освобождаются по-разному.
Объект RefCounted учитывает переменные, массивы и объекты, которые на него ссылаются. Когда счётчик падает до нуля, Godot освобождает его самостоятельно. NOTIFICATION_PREDELETE из главы «Специальные методы» показывает этот момент:
# note.gd
class_name Note
extends RefCounted
func _notification(what: int) -> void:
if what == NOTIFICATION_PREDELETE:
print("Note freed")
# main.gd
func write() -> void:
var note := Note.new()
print("Note written")
func _ready():
write() # Note written, затем Note freed
print("Back in _ready")note была единственной ссылкой, поэтому заметка освобождается, как только завершается write().
Node не учитывается. Узел в дереве сцены удаляется вместе со своим родителем, а всё дерево удаляется, когда игра заканчивается. Узел, который ты извлекаешь из дерева или никогда в него не добавляешь, нужно удалить самостоятельно:
crate.free() # освобождается сейчас вместе со всеми дочерними узлами
crate.queue_free() # освобождается в конце текущего кадраПосле free() узла больше нет: is_instance_valid(crate) возвращает false, а вызов метода для него — это ошибка. queue_free() — безопасный выбор, пока другой код может использовать узел в этом кадре, но до конца этого кадра узел остаётся в дереве и по-прежнему учитывается в get_child_count().
Узел, который никогда не освобождается, — это утечка памяти: при завершении программы Godot сообщает "ObjectDB instances were leaked at exit". Объекты RefCounted тоже вызывают утечку, если два таких объекта ссылаются друг на друга, потому что счётчик ссылок ни у одного из них не может стать равным нулю. Храни ссылку только в одном направлении или сохраняй имя вместо объекта.
Используй RefCounted для данных: предметов, очков, записей. Используй Node для объектов, которые существуют в дереве сцены.
Задание
ЛегкоУзел Wave хранит своих врагов в дочерних узлах. enemy.gd уже готов: Enemy.new("Goblin", 5) создаёт узел врага с именем Goblin и 5 HP. Напиши три метода Wave:
spawn(enemy_name, hp)добавляет нового врага как дочерний узел. Если дочерний узел с таким именем уже существует, метод выводитOrc is already hereи ничего не создаёт.hit(enemy_name, damage)уменьшаетhpэтого врага и выводитOrc has 8 HP. Если у врага осталось0HP или меньше, метод выводитOrc defeatedи сразу же удаляет врага навсегда: следующий вызовalive()не должен его учитывать. Если врага с таким именем нет, метод выводитNo Orc here.alive()возвращает количество оставшихся врагов.
Необходимо освободить каждый узел: любая утечка при завершении приводит к провалу теста. Предоставленный код добавляет волну в дерево сцены, считывает количество следующих команд (spawn Goblin 5, hit Goblin 6, count) и выводит количество оставшихся врагов. Для команд spawn Goblin 5, spawn Orc 12, hit Goblin 6, count, hit Goblin 1, spawn Orc 3, hit Orc 4 он выводит:
Goblin defeated
Enemies: 1
No Goblin here
Orc is already here
Orc has 8 HP
Enemies left: 1Твой код нужно поместить в wave.gd и enemy.gd. В main.gd находится предоставленный код ввода-вывода, его нельзя изменять.
Попробуйте сами
extends Node
func _ready():
# Предоставленный код ввода/вывода: оставьте его без изменений
var wave := Wave.new()
add_child(wave)
var count := int(OS.read_string_from_stdin())
for i in count:
var parts := OS.read_string_from_stdin().split(" ")
if parts[0] == "spawn":
wave.spawn(parts[1], int(parts[2]))
elif parts[0] == "hit":
wave.hit(parts[1], int(parts[2]))
else:
print("Enemies: ", wave.alive())
print("Enemies left: ", wave.alive())
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово selfМетодыСвойстваКонструктор (_init)Повторение: простой калькулятор4Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур7Сигналы
Что такое сигнал?Объявление и отправкаПодключение к сигналамСигналы с аргументамиПовторение — система здоровья10Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»2Свойства классов
Переменные экземпляра и классаСтатические функцииСеттеры свойствГеттеры свойствПриватность по соглашениюПовторение — менеджер банковского счёта5Инкапсуляция
Открытые и закрытые членыПроверка данных с помощью сеттеровСокрытие информацииСвойства только для чтенияПовторение — система учета студентов8Продвинутые концепции ООП
Композиция и наследованиеВнутренние классыУзлы как компонентыRefCounted и NodeПеречисления в классах11Проект: Управление библиотекой
Обзор проектаКлассы книг и пользователей3Наследование
Основы наследованияКлючевое слово superПереопределение методовЦепочки наследованияПроверка типов с помощью isПовторение — иерархия сотрудников6Специальные методы
Введение в виртуальные методыМетод _to_stringПользовательские итераторыПовторение: пользовательский список9Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»Потренируйтесь самостоятельно: Онлайн-компилятор GDScript