WorldObject
world/world-object.ts:90 Extends AbstractComponentHost<WorldObject>
A single addressable thing inside a World. A WorldObject is a
spatial node that hosts Components — controllers, visuals,
colliders, audio sources, anything else — and provides them with a
canonical, shared transform (position, rotation, scale) that they all
read from or write into.
The behaviour and appearance of an object is defined by its components. The transform fields, by contrast, are owned by the host: there is one position, one rotation, and one scale per object, regardless of how many components reference them. This is the engine's answer to the "ten components agreeing about where the thing is" coordination problem — authoritative state lives on the host, and components are either:
- Projections of the host transform — a PolygonGraphics
reading
host.position/host.rotation/host.scaleand pushing them to its underlying PIXI display object once per frame. A collider reading the same fields to transform its local shape into world space. - Writers of the host transform — controllers and AI setting
host.rotationto face a target, dynamic physics writing back simulated results inonPostUpdate.
Pick one role per component. Mixing both — having two components fight
over host.rotation in the same phase, for instance — is exactly the
coordination bug the host-owned transform exists to prevent. If two
systems both need to author rotation, decide who owns it and have the
other read.
Lifecycle
An object has three internal states: live (in the world, ticking),
marked (WorldObject.destroy has been called, awaiting the
world's sweep at the end of the current/next tick), and cleaned
(onDestroy has fired, the object is inert). Transitions are one-way and
the API is idempotent — calling destroy() repeatedly or on an
already-cleaned object is safe.
Enabling and disabling
Setting AbstractComponentHost.enabled to false on an object
gates all three of its per-frame component phases (onPreUpdate,
onUpdate, onPostUpdate) at a single early-return: a paused enemy, a
frozen UI widget, a temporarily-disabled debug overlay. The object keeps
its components and their state; flip enabled back to true and it
resumes ticking from where it was. onAdded and onDestroy are not
gated — a half-attached or half-destroyed object would be worse than a
paused one.
Example
// A controller sets the host's rotation; the graphics component reads
// it back out in the same tick (no coupling between the two).
class ChaseAI implements WorldObjectComponent {
constructor(public readonly host: WorldObject) {}
onAdded() {}
onUpdate() {
const target = this.host.world.findOneByTag('player');
if (target) {
this.host.rotation = this.host.position.angleTo(target.position);
}
}
onDestroy() {}
}Constructors
constructor(world: World, position: PointPrimitive, metadata: WorldObjectMetadata, rotation: number, scale: Point): WorldObject Parameters
-
worldWorld -
positionPointPrimitive -
metadataWorldObjectMetadata -
rotationnumber -
scalePoint
Properties
components: Map<string, Component<WorldObject, unknown>> enabled: boolean Master gate on every component update phase this host runs. When
false, the host's onPreUpdate, onUpdate, and onPostUpdate
iterations short-circuit at a single check — useful for freezing a
single object during a cutscene, pausing a UI widget while a menu is
up, or temporarily disabling a debug overlay without tearing the
components down.
The flag is not propagated to onAdded or onDestroy. Those
always fire so a host can never end up with half-attached components,
and a disabled object is still cleanly torn down when destroyed.
Defaults to true (active). Flip back to true and the host resumes
ticking from its preserved state on the next world update().
metadata: WorldObjectMetadata Metadata about the object and its relationship with the world it is part of.
position: Point The object's position in world space, in pixels. Constructed fresh from
the value passed to the constructor so external mutations of that input
cannot leak in; the Point exposed here is mutable and intended to be
written by controllers / physics / movement code
(host.position.x += dx).
rotation: number The object's rotation in world space, in radians, measured clockwise
from the positive x-axis (i.e. 0 faces right, matching the convention
used by Point.angular and Point.angleTo). Mutable —
controllers, AI and physics write into this directly; visual components
read it back to orient themselves.
Defaults to 0 (facing right) for newly-constructed objects. The
engine does not normalise the value, so callers may freely accumulate
angles past 2π if that simplifies their logic.
scale: Point The object's scale, expressed as a 2D Point so x and y can be scaled
independently. Defaults to 1,1 (no scaling). The exposed Point is
mutable in place — host.scale.x = 2 works — and components projecting
from the host transform are expected to honour both axes.
Like WorldObject.position, scale is cloned from the value passed to the constructor so the inbound point can be safely reused or mutated by the caller without affecting this object.
Accessors
destroyed: boolean Whether this object is no longer alive — either marked for destruction
and awaiting the next sweep, or already cleaned up. Live objects return
false; everything else returns true.
Methods
_createDependencyResolver(component: Component<WorldObject>, key: string): WorldObjectComponentDependencyResolver Subclass hook that produces the concrete dependency resolver the host
hands to a component's resolveDependencies. The World hosts a
resolver scoped to siblings only; a WorldObject hosts one that
also exposes cross-tier lookups against the parent world.
Engine-internal — never called by user code.
Parameters
-
componentComponent<WorldObject> -
keystring
Returns
WorldObjectComponentDependencyResolver _handleComponentDestroyError(error: unknown, key: string): void Hook for subclasses to intercept errors thrown by a component's
onDestroy during AbstractComponentHost.removeAllComponents.
Default behaviour is to log and swallow — a single bad component must
not prevent the rest of the host's components from being torn down.
Subclasses may override to route errors through their own reporting
channel.
Parameters
-
errorunknown -
keystring
Returns
void addComponent(key: string, component: Component<WorldObject>, options: AddComponentOptions): Component<WorldObject> Adds a new component to the host object. Throws if a component with the
specified key already exists. Calls onAdded() on the component once
registered with its host.
Parameters
-
keystring -
componentComponent<WorldObject> -
optionsAddComponentOptions
Returns
Component<WorldObject> addComponentFromFactory(key: string, factory: ComponentFactory<WorldObject>, options: AddComponentOptions): Component<WorldObject> Adds a new component to the host object using a factory function.
Internally produces the new component using the factory function, then
calls addComponent() with the result.
The advantage of using this method over addComponent() is that the
factory function is provided with the host.
Parameters
-
keystring -
factoryComponentFactory<WorldObject> -
optionsAddComponentOptions
Returns
Component<WorldObject> addComponents(components: ComponentMap<WorldObject>, options: AddComponentOptions): ComponentMap<WorldObject> Adds a new set of components to the host object. Throws if a component with
the specified key already exists. Calls onAdded() on each component
after they are all registered with the host, rather than one by one.
This is important for components that may want to reference each other
during the addition phase via host.getComponent() or similar methods.
It is recommended to use this method rather than addComponent() in
situations like initialization of a new host object.
Parameters
-
componentsComponentMap<WorldObject> -
optionsAddComponentOptions
Returns
ComponentMap<WorldObject> addComponentsFromFactories(map: ComponentFactoryMap<WorldObject>, options?: AddComponentOptions): ComponentMap<WorldObject> Adds a new set of components to the host object based on an input map of
component keys to factory functions. Behavious is equivalent to
addComponents() using the key and output of each factory function.
Parameters
-
mapComponentFactoryMap<WorldObject> -
options?AddComponentOptions
Returns
ComponentMap<WorldObject> destroy(): void Marks the object as destroyed. This does not immediately remove it from the world or destroy its components — the world must tick at least once for that to happen.
If called during a World.update tick, the object is removed at
the end of that tick. If the object has not yet had its onUpdate called
during the same tick (e.g. it was destroyed by a component or by an
earlier object in the iteration), its onUpdate is skipped — destroyed
objects do not get one final tick.
Calling destroy on an already-marked or already-cleaned object is a
no-op.
Returns
void getComponent(key: string): T Gets a component from the host object using the key it was registered with.
Throws if the component does not exist. Performs an efficient lookup on a
local Map instance.
Parameters
-
keystring
Returns
T getComponentByType(type: ComponentHostConstructor<T>): T Gets a component from the host object using its type. Throws if no
component of the type exists, or if more than one component of the type
exists — in the multi-match case, getComponentByType deliberately
does not pick one for you. Use ComponentHost.getComponentsByType
when you genuinely expect multiple matches, or look the component up by
its string key.
Performs an O(n) lookup once per type initially, then caches the resolved key for O(1) lookups on subsequent calls. The cache is invalidated whenever a component is removed.
Parameters
-
typeComponentHostConstructor<T>
Returns
T getComponentsByType(type: ComponentHostConstructor<T>): readonly T[] Gets every component on the host of the given type, in the order they were originally registered. Returns an empty array if no components match.
Unlike ComponentHost.getComponentByType, this method never throws on multiplicity — it is the explicit "I expect more than one" accessor.
Parameters
-
typeComponentHostConstructor<T>
Returns
readonly T[] getHostReference(): WorldObject Gets a reference to the host object that this component is attached to. Required for the compiler to be able to resolve the type of the host object correctly in some internal function calls (e.g. when resolving the type of the host object from a factory function).
Returns
WorldObject getNullableComponent(key: string): null | T Gets a component from the host object using the key it was registered with.
Returns null if the component does not exist, rather than throwing an
error. Useful for referencing transient or optional components without
manually handling errors.
Parameters
-
keystring
Returns
null | T getNullableComponentByType(type: ComponentHostConstructor<T>): null | T Gets a component from the host object using its type. Returns null if
the component does not exist or if more than one component of the type
is registered (i.e. the lookup is ambiguous) — the nullable variant
collapses both "not found" and "ambiguous" into a single null. Use
ComponentHost.getComponentsByType when you need to distinguish
them.
Parameters
-
typeComponentHostConstructor<T>
Returns
null | T hasComponent(key: string): boolean Checks if the host object has a component with the specified key.
Parameters
-
keystring
Returns
boolean hasComponentByType(type: ComponentHostConstructor<T>): boolean Checks if the host object has a component with the specified type.
Parameters
-
typeComponentHostConstructor<T>
Returns
boolean localToWorld(point: PointPrimitive): Point Maps a point expressed in this object's local space into the
world's coordinate system. The forward transform applied — in order —
is scale, rotate, then translate by WorldObject.position, so
(0, 0) always maps to the host's world position, and a local point
"10 units along +x" lands wherever the host is currently facing,
scaled to whatever the host's scale.x is.
Pairs with WorldObject.worldToLocal — round-tripping a point through both methods is the identity (modulo floating-point error).
Allocates a fresh Point per call so callers can mutate the result without affecting host state.
Parameters
-
pointPointPrimitive
onDestroy(): void Lifecycle hook called when this object is actually removed from the world. Idempotent — repeat invocations are no-ops, so callers can fire it defensively without worrying about double-cleanup of components.
Returns
void onPostUpdate(update: WorldUpdate): void Drives the onPostUpdate phase across this object's components.
Called by the World during the post-update pass of each tick.
Skips components whose enabled is explicitly false, and components
that do not implement the optional hook.
Parameters
-
updateWorldUpdate
Returns
void onPreUpdate(update: WorldUpdate): void Drives the onPreUpdate phase across this object's components. Called
by the World during the pre-update pass of each tick. Skips
components whose enabled is explicitly false, and components that
do not implement the optional hook.
Parameters
-
updateWorldUpdate
Returns
void onUpdate(update: WorldUpdate): void Drives the onUpdate phase across this object's components. Called by
the World during the main update pass of each tick. Skips
components whose enabled is explicitly false.
Parameters
-
updateWorldUpdate
Returns
void removeAllComponents(): void Removes all components from the host object. Typically called internally
when the lifecycle of the host object is terminated. Differs from
individually removing components in that it first calls onDestroy() on
each component, then removes references from the host object in a separate
step. This allows cleaner teardown of components that may reference each
other.
Returns
void removeComponent(key: string): void Removes a component from the host object. Care should be taken when
manually removing components, as methods like getComponent() will throw
if components do not exist. Removal is idempotent and will do nothing if
the component does not exist i.e. was already removed, or never existed.
Parameters
-
keystring
Returns
void worldToLocal(point: PointPrimitive): Point Maps a point expressed in world space into this object's local coordinate system — the inverse of WorldObject.localToWorld. This is the primitive that hit-tests and other shape-vs-world queries are built on: convert the world point to local, then ask the local- space shape (a Polygon, a Circle, ...) whether it contains it.
Axes with zero WorldObject.scale are left untouched on that axis (rather than dividing by zero); a fully zero-scaled object collapses to a point and containment against it is undefined either way, so this is just the cheaper of two equally-degenerate behaviours.
Allocates a fresh Point per call.
Parameters
-
pointPointPrimitive