Уведомления: дерево, родитель и удаление
Когда нужен _Notification, чем Parented отличается от EnterTree и как не имитировать жизненный цикл.
Использовать специальный callback, если он есть
Для входа, готовности, кадра и рисования обычно понятнее _EnterTree, _Ready, _Process и _Draw. _Notification(int what) нужен, когда требуется уведомление без отдельного удобного callback либо свой протокол уведомлений. Используйте имена констант SDK вместо чисел, выбранных по памяти.
| Уведомление | Что произошло |
|---|---|
| NotificationParented | Назначен родитель; дерево может быть целиком вне активной игры |
| NotificationUnparented | Родитель удалён; это само по себе не native-удаление объекта |
| NotificationPredelete | Движок освобождает native-объект |
| NotificationReady | Готовность в протоколе Entity; обычно используйте _Ready |
Наблюдать настоящие события
Запустите тот же LifecycleDemo из руководства по жизненному циклу. Для самостоятельного копирования оба полных файла приведены здесь. Journal содержит parented/unparented наряду с enter/ready/exit; при native-освобождении добавляется predelete.
LifecycleProbe.cs
using System.Collections.Generic;
using Reckfell;
public partial class LifecycleProbe : Entity
{
public List<string> Journal { get; set; } = new();
public int Entries { get; private set; }
public int ReadyCalls { get; private set; }
public int Exits { get; private set; }
private void Record(string stage) => Journal.Add(Name + ":" + stage);
public override void _EnterTree() { Entries++; Record("enter"); }
public override void _Ready() { ReadyCalls++; Record("ready"); }
public override void _ExitTree() { Exits++; Record("exit"); }
public override void _Notification(int what)
{
if (what == NotificationParented) Record("parented");
if (what == NotificationUnparented) Record("unparented");
if (what == NotificationPredelete) Record("predelete");
}
}LifecycleDemo.cs
using System.Collections.Generic;
using Reckfell;
public partial class LifecycleDemo : Entity
{
public List<string> Journal { get; } = new();
public LifecycleProbe ParentProbe { get; private set; } = null!;
public LifecycleProbe ChildProbe { get; private set; } = null!;
public override void _Ready()
{
ParentProbe = new LifecycleProbe { Name = "Parent", Journal = Journal };
ChildProbe = new LifecycleProbe { Name = "Child", Journal = Journal };
ParentProbe.AddChild(ChildProbe);
AddChild(ParentProbe);
RemoveChild(ParentProbe);
AddChild(ParentProbe);
RemoveChild(ParentProbe);
ChildProbe.RequestReady();
AddChild(ParentProbe);
ReckfellApi.Print(string.Join(" → ", Journal));
ReckfellApi.Print("Ready calls: Parent=" + ParentProbe.ReadyCalls + ", Child=" + ChildProbe.ReadyCalls);
}
}AddChild к отсоединённому родителю уже вызывает Parented у ребёнка, но ещё не включает его в EntityTree и не вызывает _EnterTree. RemoveChild из такого дерева даёт Unparented без _ExitTree, потому что активного входа не было. Стенд проверяет эти случаи отдельно, а не выводит их из имени уведомления.
Predelete не является C#-финализатором и не обещает время работы сборщика мусора. Для C#-скрипта RefCounted проверенный мост пропускает Predelete: не используйте этот callback как гарантированный очиститель Resource. Пример выше относится к Entity. Для обычной Entity отвечайте за native-жизнь через дерево, QueueFree/Free и явное владение отсоединёнными объектами. Для снятия игровых подписок используйте _ExitTree: объект может выйти из уровня и остаться живым в кэше.
Собственное уведомление
В примере групп GroupTarget2D объявляет RefreshNotification = 10001. NotifyGroup доставляет это значение в _Notification. Значение выбрано для собственного протокола примера; проверяйте конфликты в своём проекте.
Notification не является сигналом с произвольными аргументами: обработчик получает код what. Не отправляйте системный NotificationEnterTree/Ready, пытаясь вручную подключить объект. Настоящие AddChild/RemoveChild выполняют дополнительную работу с деревом, миром, вводом и группами; один номер её не заменяет.