Behavior lifecycle: On enable, On disable and On destroy events (#42)
game-maker / suite (push) Successful in 7m29s
game-maker / suite (push) Successful in 7m29s
Order per instance: On start -> On enable -> On update ... -> On disable -> On destroy. On enable fires right after On start and whenever the behavior comes back on (itself and its parents); On disable in the frame it goes off, the instance stays frozen. On destroy fires when the entity leaves the world (Destroy node, child of a destroyed object) and when the world is torn down on stop/restart, preceded by On disable if still on. A new On start node added while playing still does not fire for a running instance (kept as is, documented in the wiki). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
f0673d1957
commit
6141d07d4f
8 files changed
+203
-8
No files matched your search
@@ -43,6 +43,8 @@ interface Instance {
|
||||
values: Map<Graph, Map<string, Produced>>;
|
||||
/** Current state per state machine – this instance's alone, like its variables. */
|
||||
states: Record<string, string>;
|
||||
/** Whether the behavior was effectively on when last looked at; a change fires On enable/On disable. */
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +106,9 @@ export class BehaviorSystem implements System {
|
||||
this.syncInstances(world);
|
||||
}
|
||||
|
||||
/** Tearing the world down (stop, restart) destroys every object, so each instance hears it. */
|
||||
stop(): void {
|
||||
for (const instance of this.instances) this.finish(instance);
|
||||
this.instances = [];
|
||||
this.world = null;
|
||||
}
|
||||
@@ -114,7 +118,7 @@ export class BehaviorSystem implements System {
|
||||
* switched on, which is what lets a hidden screen wake up when shown.
|
||||
*/
|
||||
private syncInstances(world: World): void {
|
||||
this.instances = this.instances.filter((i) => world.entities.includes(i.entity));
|
||||
this.reap(world);
|
||||
for (const entity of world.entities) {
|
||||
if (
|
||||
entity.type !== 'behavior' ||
|
||||
@@ -137,19 +141,58 @@ export class BehaviorSystem implements System {
|
||||
byNode.set(id, { time: world.time, values: produced });
|
||||
},
|
||||
} satisfies TraceListener;
|
||||
const instance: Instance = { entity, interpreter, trace, values, states: {} };
|
||||
const instance: Instance = { entity, interpreter, trace, values, states: {}, active: true };
|
||||
this.instances.push(instance);
|
||||
this.run(instance, (i, ctx) => i.fire('OnStart', ctx), 0);
|
||||
this.run(
|
||||
instance,
|
||||
(i, ctx) => {
|
||||
i.fire('OnStart', ctx);
|
||||
i.fire('OnEnable', ctx);
|
||||
},
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Instances whose entity left the world get their last events and are dropped. */
|
||||
private reap(world: World): void {
|
||||
const gone = this.instances.filter((i) => !world.entities.includes(i.entity));
|
||||
if (gone.length === 0) return;
|
||||
this.instances = this.instances.filter((i) => !gone.includes(i));
|
||||
for (const instance of gone) this.finish(instance);
|
||||
}
|
||||
|
||||
private finish(instance: Instance): void {
|
||||
this.run(
|
||||
instance,
|
||||
(i, ctx) => {
|
||||
if (instance.active) i.fire('OnDisable', ctx);
|
||||
instance.active = false;
|
||||
i.fire('OnDestroy', ctx);
|
||||
},
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Follows the effective switch – off when the behavior or a parent is off, or when "run in editor"
|
||||
* was taken back while running – and fires the edge it just crossed.
|
||||
*/
|
||||
private switched(instance: Instance): boolean {
|
||||
const on = isHandleEnabled(instance.entity) && this.shouldRun(instance.entity);
|
||||
if (on !== instance.active) {
|
||||
instance.active = on;
|
||||
this.run(instance, (i, ctx) => i.fire(on ? 'OnEnable' : 'OnDisable', ctx), 0);
|
||||
}
|
||||
return on;
|
||||
}
|
||||
|
||||
update(world: World, dt: number): void {
|
||||
this.syncInstances(world);
|
||||
const keyOf = (node: { params: PropertyBag }) => textOf(node.params.key ?? '');
|
||||
for (const instance of this.instances) {
|
||||
if (!world.entities.includes(instance.entity)) continue; // destroyed during play
|
||||
if (!isHandleEnabled(instance.entity)) continue; // switched off (itself or a parent)
|
||||
if (!this.shouldRun(instance.entity)) continue; // "run in editor" taken back while running
|
||||
if (!world.entities.includes(instance.entity)) continue; // destroyed this frame, reaped below
|
||||
if (!this.switched(instance)) continue;
|
||||
this.run(
|
||||
instance,
|
||||
(i, ctx) => {
|
||||
@@ -180,6 +223,7 @@ export class BehaviorSystem implements System {
|
||||
dt,
|
||||
);
|
||||
}
|
||||
this.reap(world);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user