feat(world): add singleton component support
Introduce a mechanism to manage components on a shared, lazily-created singleton entity. This simplifies access to global state by providing dedicated methods for adding, removing, getting, and checking for singleton components. Refactor the Tetris example to utilize this new singleton pattern for game state components like Board, Score, and Piece.
This commit is contained in:
@@ -42,6 +42,9 @@ export class World {
|
||||
// ── Observable layer ──────────────────────────────
|
||||
private _observable = new ObservableLayer();
|
||||
|
||||
// ── Singleton entity ──────────────────────────────
|
||||
private _singletonEntity: Entity | null = null;
|
||||
|
||||
/** Global event stream. */
|
||||
get events$(): Observable<WorldEvent> {
|
||||
return this._observable.events$.asObservable();
|
||||
@@ -250,6 +253,69 @@ export class World {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Singleton component access ────────────────────
|
||||
|
||||
/**
|
||||
* Add a singleton component to the world.
|
||||
*
|
||||
* A single shared entity is created lazily and reused for all singleton
|
||||
* components. Returns a mutable reference to the component data.
|
||||
*/
|
||||
addSingleton<T extends Record<string, any>>(
|
||||
def: ComponentDef<T>,
|
||||
init?: Partial<T>,
|
||||
): T {
|
||||
if (this._singletonEntity === null) {
|
||||
this._singletonEntity = this.spawn();
|
||||
}
|
||||
return this.add(this._singletonEntity, def, init);
|
||||
}
|
||||
|
||||
/** Remove a singleton component. Destroys the backing entity if it becomes bare. */
|
||||
removeSingleton(def: ComponentDef<any>): void {
|
||||
const e = this._singletonEntity;
|
||||
if (e === null) return;
|
||||
this.remove(e, def);
|
||||
if (!this.hasAnyComponent(e)) {
|
||||
this.destroy(e);
|
||||
this._singletonEntity = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get a mutable reference to a singleton component. Throws if missing. */
|
||||
getSingleton<T extends Record<string, any>>(def: ComponentDef<T>): T {
|
||||
return this.get(this._singletonEntity!, def);
|
||||
}
|
||||
|
||||
/** Try-get a singleton component. Returns undefined if missing. */
|
||||
tryGetSingleton<T extends Record<string, any>>(
|
||||
def: ComponentDef<T>,
|
||||
): T | undefined {
|
||||
const e = this._singletonEntity;
|
||||
if (e === null) return undefined;
|
||||
return this.tryGet(e, def);
|
||||
}
|
||||
|
||||
/** Check whether a singleton component is present. */
|
||||
hasSingleton(def: ComponentDef<any>): boolean {
|
||||
const e = this._singletonEntity;
|
||||
if (e === null) return false;
|
||||
return this.has(e, def);
|
||||
}
|
||||
|
||||
/** Bulk-replace a singleton component's data. Marks dirty. */
|
||||
setSingleton<T extends Record<string, any>>(
|
||||
def: ComponentDef<T>,
|
||||
value: T,
|
||||
): void {
|
||||
this.set(this._singletonEntity!, def, value);
|
||||
}
|
||||
|
||||
/** Mark a singleton component as dirty for change tracking. */
|
||||
markDirtySingleton(def: ComponentDef<any>): void {
|
||||
this.markDirty(this._singletonEntity!, def);
|
||||
}
|
||||
|
||||
// ── Relationships ─────────────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user