Entity lifecycle: where code belongs
EnterTree, Ready and ExitTree order; reentry, RequestReady and a complete C# fixture.
Choose a callback
| Task | Place |
|---|---|
| Initial field values | Field initializer/constructor; do not assume an active tree |
| Subscribe on every entry | _EnterTree |
| Configure after children are ready | _Ready |
| Disconnect external handlers on exit | _ExitTree |
| Ordinary frame updates | _Process(double delta) |
| Physics-step logic | _PhysicsProcess(double delta) |
| Handle input not consumed earlier | _UnhandledInput / _UnhandledKeyInput |
| Draw a CanvasItem | _Draw; request a redraw with QueueRedraw |
Names and casing matter. In C# use public override and the exact SDK signature. A constructor does not mean the object is already in a level. Do not use it to locate children that still need loading from a prefab. If your own C# base class implements the callback, explicitly decide whether to call base; an empty Entity base method is not a required ritual.
Verify the order
Add both files to a Reckfell 1.0.0 C# project. Attach LifecycleDemo to an Entity in a separate level, build and press F6. Output shows the journal and Ready calls: Parent=1, Child=2. Initial entry/readiness events are Parent:enter → Child:enter → Child:ready → Parent:ready.
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);
}
}Exit calls Child:_ExitTree before Parent:_ExitTree. RemoveChild does not delete an object; a later AddChild calls _EnterTree again without automatically repeating ordinary _Ready. RequestReady affects the requesting Entity next entry: it neither calls _Ready immediately nor recursively applies to children.
Subscriptions and dependencies
If a new child emits a signal in _Ready, connect before AddChild. For an external long-lived source, the _EnterTree/_ExitTree pair supports reattachment without duplicate handlers. Subscribing only in _Ready and disconnecting in _ExitTree is insufficient for reentry.
_Ready guarantees readiness of ordinary existing children, not future dynamic objects or arbitrary external services. Inject dependencies before activation or define a separate initialization phase. Pause does not cancel a direct method call or an emitted signal: see pause rules.
The fixture verifies actual callbacks, three entries, two exits, RequestReady and native release. This journal does not establish keyboard routing or every Resource lifetime behavior.