Skip to content

Script

Component holding the list of scripts attached to an entity. Loaded from the saved project; the runtime-side fields (scriptInstance, scriptInstances) are non-enumerable so they never leak into save data or undo history. Authored scripts subclass Behaviour.

Managed by: ScriptBehaviourSystem

Editor inspector

Script inspector

Properties

PropertyTypeDescription
entriesScriptEntry[]Authored script entries attached to the entity. Each entry references a class by name plus its inspector field overrides. Saved with the project.
scriptInstanceunknown | nullFirst live behaviour instance after compile/load. Non-enumerable, so it does not appear in save data. null when no script is loaded.
scriptInstancesMap<number, unknown>Map of entityId to its compiled behaviour instance (one entry per script bound to the entity). Non-enumerable, so it does not appear in save data.

API reference

Scripting examples

Component access patterns

Use this.getComponent(X) to access a component on the entity this script is attached to. Use this.world.getEntityByName to find another entity by name, then this.world.getComponent to read its component:

typescript
// Component on the entity this script is on
const scriptComp = this.getComponent(ScriptComponent);

// Component on another entity, found by its scene name
const other = this.world.getEntityByName("Manager");
if (other) {
  const scriptComp = this.world.getComponent(other.entityId, ScriptComponent);
}

Call a method on another script attached to the same entity

ScriptComponent.scriptInstances maps entity IDs to their compiled behaviour instances. You can cast an instance to a known shape and call public methods on it, enabling script-to-script communication.

typescript
import { Behaviour, ScriptComponent } from "@relu-interactives/spatial-ecs";

// Assumes another script on this entity exposes a `reset()` method.
export default class ScriptBridge extends Behaviour {
  protected init() {
    const scriptComp = this.getComponent(ScriptComponent);
    if (!scriptComp) return;

    // scriptInstance is the first compiled behaviour on this entity
    const other = scriptComp.scriptInstance as { reset?(): void } | null;
    if (other?.reset) {
      other.reset();
    }
  }
}

Inspect script entries

Read the authored entries array to find which scripts are attached and inspect their saved field overrides.

typescript
import { Behaviour, ScriptComponent } from "@relu-interactives/spatial-ecs";

export default class ScriptInspector extends Behaviour {
  protected init() {
    const scriptComp = this.getComponent(ScriptComponent);
    if (!scriptComp) return;

    for (const entry of scriptComp.entries) {
      console.log(`Script: ${entry.className}`, entry.data);
    }
  }
}

Via ComponentInput in the inspector

You can also pick this component from the inspector using a ComponentInput field. Assign any entity in the inspector and the field resolves to the live ScriptComponent instance at runtime.

typescript
import {
  Behaviour,
  ScriptComponent,
  type ComponentInput,
} from "@relu-interactives/spatial-ecs";

export default class Example extends Behaviour {
  data = {
    targetScript: {
      type: "component",
      value: null,
    } as unknown as ComponentInput,
  };

  protected onUpdate() {
    const scriptComp = this.data.targetScript.value as ScriptComponent | null;
    if (!scriptComp) return;
    // Use the live component instance.
  }
}

Via EntityInput in the inspector

Alternatively, pick an entity from the inspector using an EntityInput field and then read the component off that entity via world.getComponent:

typescript
import {
  Behaviour,
  ScriptComponent,
  type EntityInput,
  type WorldEntityView,
} from "@relu-interactives/spatial-ecs";

export default class Example extends Behaviour {
  data = {
    targetEntity: { type: "entity", value: null } as unknown as EntityInput,
  };

  protected onUpdate() {
    const entity = this.data.targetEntity.value as WorldEntityView | null;
    if (!entity) return;

    const scriptComp = this.world.getComponent(
      entity.entityId,
      ScriptComponent,
    ) as ScriptComponent | null;
    if (!scriptComp) return;
    // Use the component on the picked entity.
  }
}

Runtime vs. authored data

entries is the persisted array of script definitions (class name + inspector overrides). scriptInstance and scriptInstances are non-enumerable runtime references that are never saved to snapshots. Do not store references to scriptInstance across frames — re-read from ScriptComponent each time if needed.