Skip to content

ScriptBehaviourSystem ​

Loads user-authored Behaviour scripts via dynamic import(), instantiates their classes, and drives the full Behaviour lifecycle (init, onUpdate, onDestroy) each frame.

Overview ​

ScriptBehaviourSystem runs every frame and is responsible for:

  • Discovering entities with a ScriptComponent and iterating their entries array.
  • Dynamically importing each script's compiled URL the first time it is seen (or when the URL changes).
  • Resolving the exported Behaviour subclass by name (or falling back to the default export).
  • Instantiating the class and calling init() once.
  • Calling onUpdate(deltaTime) on every active script instance each frame.
  • Detecting stale instances (removed entries, changed URLs, destroyed entities) and calling onDestroy() before discarding them.

Queried components ​

ComponentAccess
ScriptComponentRead (entries, defaultData, runtimeData)

Behaviour resolution ​

The system resolves the Behaviour constructor from the imported module using:

  1. Named export matching ScriptEntry.className.
  2. default export as a fallback.
  3. A __reluBehaviourClass static marker check to handle cases where esbuild bundles a second copy of @relu-interactives/spatial-ecs into the script blob (breaking the instanceof identity check).

If no valid Behaviour subclass is found the entry is skipped and an error is written to the console.

Behavior notes ​

  • Instance keying — each script instance is keyed by entityId + scriptUrl + className. A new instance is created only when any of these three values changes.
  • Error isolation — errors thrown inside init() or onUpdate() are caught per-instance and logged to the console. A failing script does not crash the frame loop or affect other scripts.
  • Destroyed entity cleanup — when an entity is removed from the world, all of its script instances are destroyed and removed from the registry on the next update.
  • Editor vs preview — Behaviours execute only at preview/runtime. In an editor world (isEditorMode()), every ScriptComponent entry stays in idle state and init()/onUpdate() never run. For visuals that must render in the editor, use ScriptObjectSystem + ScriptObject.

API reference ​