Entity, уровни и префабы
Родитель, Owner, создание экземпляра, порядок событий и удаление.
Entity — объект иерархии игры: у него есть имя, свойства, родитель и дочерние объекты. К нему можно подключить C#-скрипт. Entity2D добавляет преобразование в 2D, Entity3D — в 3D; камера, физическое тело и элемент интерфейса используют специализированные типы.
Это объектная иерархия, не ECS. Название Entity само по себе не означает хранение компонентов в таблицах или автоматическую обработку системами.
Соберите объект по назначению
Для управляемого персонажа 3D типичная структура выглядит так:
Player (CharacterBody3D)
├── CollisionShape3D
├── MeshInstance3D
└── Camera3DТело выполняет физическое движение, форма задаёт коллизию, меш — внешний вид, камера — точку обзора. Один компонент не заменяет остальные. Готовый пример движения: персонаж 3D.
Дочерние пространственные объекты обычно наследуют преобразование родителя. Position — локальная позиция, GlobalPosition — глобальная. В выполненном примере родитель расположен в (10, 0, 0), дочерний объект — локально в (1, 2, 3); его глобальная позиция равна (11, 2, 3). Не применяйте формулу простого сложения к повёрнутому или масштабированному родителю.
Уровень, префаб и экземпляр
| Что | Назначение |
|---|---|
.rscene | Файл иерархии, используемый как уровень проекта. |
.rprefab | Файл переиспользуемой иерархии: персонаж, предмет, интерфейс или окружение. |
PackedEntity | Загруженный ресурс иерархии; из него создаются экземпляры обоих форматов. |
| Экземпляр | Созданные Entity в памяти, которые можно подключить к дереву. |
Расширение выражает роль файла; это не два разных класса ресурса и не жёсткий запрет загрузить префаб через API смены уровня. В собственном коде сохраняйте разделение ролей: уровень меняет текущее игровое пространство, префаб создаёт объект внутри него.
Порядок создания: загрузить PackedEntity, создать корень через Instantiate(), задать начальные свойства и подключить через AddChild(). Сам вызов Instantiate() не добавляет объект к игре. Полный пример создания префаба проверяет тип корня, начальную позицию и ошибочный ресурс.
Два экземпляра имеют отдельные Entity, но некоторые ресурсы могут быть общими. Перемещение одного не меняет файл префаба; изменение общего материала может повлиять на оба экземпляра. Ресурсы и независимые копии.
Когда запускается скрипт
| Событие | Порядок и применение |
|---|---|
_EnterTree() | Родитель входит раньше детей. Выполняется снова при повторном входе. |
_Ready() | Дети готовы раньше родителя. Обычно здесь получают обязательные дочерние ссылки. Для того же экземпляра автоматически повторно не вызывается. |
_PhysicsProcess(double delta) | Физические шаги: управление телом и физические запросы. |
_Process(double delta) | Обновления кадров: задачи, которым не нужен физический шаг. |
_ExitTree() | Дети выходят раньше родителя. Сам выход не означает удаление объекта. |
Если родитель уже находится в активном дереве, AddChild() может выполнить вход и _Ready() до возвращения из вызова. Поэтому свойства, нужные в _Ready(), задавайте заранее. Локальную позицию можно установить до подключения; глобальная зависит от будущего пространственного родителя.
В проверке порядок первого подключения был enter:Parent, enter:Child, ready:Child, ready:Parent. Повторное подключение дало новый вход без нового _Ready(). Для повторной подготовки есть RequestReady(): он запрашивает её для следующего входа этой Entity, а не немедленно и не для всех детей. Жизненный цикл подробнее.
Родитель, Owner и удаление
AddChild()назначает родителя. Новый объект не может одновременно находиться у двух родителей.RemoveChild()отсоединяет объект, но оставляет его в памяти. Сохраните ссылку для повторного использования или освободите объект.QueueFree()ставит объект и его дочернюю иерархию на удаление. До обработки очереди нативный объект ещё может существовать; не продолжайте использовать его как действующий игровой объект.Ownerзадаёт принадлежность сохраняемой иерархии, а не время жизни.AddChild()не назначает его автоматически. Для программного сохранения назначайте владельца после подключения; владелец должен быть предком.
В проверке отсоединённый родитель оставался действительным; после обработки удаления исчезли и родитель, и ребёнок. C#-ссылка сама по себе не удерживает освобождённую нативную Entity. Для сохранения составного объекта используйте Owner и упаковку.
Если что-то не сработало
Объект создан, но не обновляется — проверьте подключение к активному дереву. Позиция неверна — различайте локальные и глобальные координаты. _Ready() не повторяется — это тот же экземпляр, а не новая загрузка. Ребёнок не попал в файл — проверьте Owner. После RemoveChild() растёт число объектов — отсоединение не освобождает память.
Выполнены проверки порядка событий, повторного входа, очереди удаления, создания экземпляра, начального преобразования и общего ресурса. Операции мышью в редакторе и экспорт на другие платформы этой проверкой не оценивались.