← Index · Prev

14 — API Guide (runtime)

Reference for behavior authors and anyone reading runtime code. Import from @bjs/engine in app behavior files. This guide covers the kit’s public surface — not every Babylon.js API.

Quick example

import { Behavior, exposed, inputMap, type Entity } from "@bjs/engine";
import type { InputActionMap } from "@bjs/engine";
import { Vector3 } from "@babylonjs/core";

export default class Patrol extends Behavior
{
  @exposed({ min: 0, max: 10 }) speed = 2;
  @exposed({ type: "entity" }) target!: Entity;
  @inputMap("Player") player!: InputActionMap;

  OnStart(): void
  {
    // target is resolved before this runs
  }

  OnUpdate(deltaSeconds: number): void
  {
    const move = this.player.FindAction("Move")?.ReadVector2();
    if (move !== undefined)
    {
      this.node.position.addInPlace(new Vector3(move.x, 0, move.y).scale(this.speed * deltaSeconds));
    }
  }

  OnMessage(message: string, source: Entity): void
  {
    if (message === "Alert")
    {
      // react to trigger or GUI click from source
    }
  }
}

Behavior

Base class for all scripts. One export default class per file; filename stem = Blender registry key (Patrol.ts"Patrol").

Lifecycle (PascalCase required)

MethodWhen
OnStart(): voidOnce, after load finishes and all @exposed entity refs resolve
OnUpdate(deltaSeconds: number): voidEvery frame, inside Level.RunFrame (before physics step)
OnDestroy(): voidWhen the level is disposed
OnMessage(message, source): voidWhen this entity receives a message (Event Messages, 3D GUI, SendMessage)
OnCollisionEnter(other, contact): voidSolid collider first contacts another entity (contact is a CollisionContact: point, normal, impulse, distance)
OnCollisionStay(other, contact): voidWhile solid contact continues (Havok COLLISION_CONTINUED; stops when bodies sleep)
OnCollisionExit(other): voidSolid contact ends
OnTriggerEnter(other): voidTrigger volume on this entity first overlapped
OnTriggerExit(other): voidTrigger overlap ends (no OnTriggerStay — track overlap state in code if needed)

Physics hooks are opt-in: override one and the engine enables the matching Havok callbacks automatically — no manual observable subscription. Both bodies in a solid contact receive collision hooks (Unity semantics); trigger hooks fire on the trigger entity's behaviors. Details: 06 — Physics › Collision lifecycle hooks.

Injected members

MemberTypeWhen set
entityEntityBefore OnStart
sceneBabylon SceneBefore OnStart
nodeTransformNode (getter)Same as entity.node
input?InputActionMapScene default map when the script has no @inputMap fields

Scale continuous motion by deltaSeconds. Babylon velocities are already per-second — do not multiply those by deltaSeconds.

Entity

One Blender object that exported successfully. File: packages/engine/src/core/Entity.ts.

Fields

FieldDescription
idGUID string (bjs_id)
nameObject name at export
nodeBabylon node (mesh, empty, camera, …)
tagFrom TAG component (default "Untagged")
attachmentsAll applied component rows (see Attachments)
behaviorsLive Behavior instances from SCRIPT rows
body?Havok PhysicsBody when physics was built
animationsAnimationGroups scoped to this node
soundsStaticSounds from AUDIO components
guiTextures2D GUI textures from GUI components
controls3D3D GUI controls from GUI3D_* components
particleSystemsParticle systems from PARTICLE components
textRenderersMSDF renderers from MSDF_TEXT components
reflectionProbesReflection probes from REFLECTION_PROBE components

Methods

MethodReturnsNotes
GetAttachments()readonly attachment rowsFull list
GetAttachment(type)First row of type, or undefinede.g. GetAttachment("SCRIPT")?.behavior
GetAttachmentsOfType(type)All rows of typee.g. multiple COLLIDER rows
HasAttachment(type)boolean
GetBehavior(Ctor)First instance of classGetBehavior(Patrol)
GetAnimation(name)AnimationGroup?Exact name, then substring match
GetSound(name)StaticSound?File stem match
GetGui(name)AdvancedDynamicTexture?JSON file stem
GetControl3D(name)Control3D?Control name
GetParticles(name)IParticleSystem?System JSON stem
GetTextRenderer(name)TextRenderer?Font JSON stem
GetReflectionProbe()ReflectionProbe?First probe attachment on this entity
GetReflectionProbes()ReflectionProbe[]All probe attachments
SendMessage(message, source)voidCalls OnMessage on every behavior on this entity

Level

Runtime container returned by LevelLoader.Load. File: packages/engine/src/core/Level.ts.

Fields (common)

FieldDescription
entitiesMap<string, Entity> by GUID
activeCamera?Exported / typed active camera
shadowGeneratorsOne per casting light
punctualLightingMode"forward" | "clustered" | "forward-expanded"
post?Post-processing handles
atmosphere?Atmosphere addon handle
constraintsBuilt physics joints
clusteredLights?Clustered-light container when clustering ran
debugEnabledFrom manifest debug flag
gui3DManager?Shared 3D GUI manager
collisionEventHandles?Havok collision/trigger observers (removed on dispose)
particleEmitterManager?Empty-emitter tracking handle
msdfTextManager?MSDF text render-pass handle
reflectionProbesAll built reflection probes (dispose with level)

Methods

MethodDescription
ById(id)Look up entity by GUID
ByTag(tag)All entities with matching entity.tag
ShowColliders(show?)Toggle Havok wireframe debug view
RefreshShadows()Re-bake frozen shadow maps after moving casters
AddUpdater(fn)Per-frame callback after all OnUpdate (cameras use this)
Dispose()Teardown — input detach, OnDestroy, dispose assets

Begin() and RunFrame() are called by the loader — behaviors do not call them.

Attachments

Each successfully applied component becomes one row on entity.attachments. Types match manifest component types plus GUI3D variants.

typedataRuntime object on row
TAGtag string
RENDERING_GROUPgroup id + propagation flags— (values applied to owned meshes in FinalizeLevel)
LAYER_MASKmask + propagation flags— (values applied to owned meshes in FinalizeLevel)
COLLISION_LAYERlayer name + propagation flags— (Havok filter masks applied in FinalizeLevel)
COLLIDERcollider manifestbody (shared per entity)
RIGIDBODYbody manifestbody (same ref as colliders)
SCRIPTscript + varsbehavior
AUDIOaudio manifestsound
GUIgui manifesttexture
PARTICLEparticle manifestsystem, optional emptyEmitter
MSDF_TEXTfont paths + textrenderer
REFLECTION_PROBEprobe capture + influence settingsprobe (ReflectionProbe)
CONSTRAINTconstraint manifestconstraint
GUI3D_*layout + eventscontrol

Convenience arrays on Entity (sounds, behaviors, …) mirror attachments for common access patterns.

@exposed (Blender-editable fields)

Decorator: exposed(options?) from @bjs/engine. Must stay lowercase — Blender regex-parses the token literally.

Type / optionBlender UIRuntime value
float / int (+ min, max, step)Number slidernumber
boolCheckboxboolean
stringText fieldstring
type: "vector2"XY fields[number, number]
vector3 or [n,n,n] defaultXYZ fieldsVector3
color or Color3 defaultColor pickerColor3
type: "entity"Object pickerEntity (resolved before OnStart)
type: "enum", options: [...]Dropdownstring
type: "list", of: "entity"|…List rowsArray (entity slots resolve by index)

Defaults in source must be single-line literals so Blender’s parser can read them. After changing fields, press Sync on the SCRIPT component.

Input (@inputMap + polling)

See 12 — Input for authoring (stick vs 1D/2D composites, control type, gamepad labels, LT/RT as axis 4/5, axisHalf, disambiguation). In behaviors:

@inputMap("Player") player!: InputActionMap;  // named map
@inputMap("Vehicle") vehicle!: InputActionMap; // e.g. CarController

// Vector 2 action — Type: Value, Control Type: Vector 2 in Blender
const control = this.vehicle.FindAction("Main Control")?.ReadVector2() ?? { x: 0, y: 0 };
const throttle = control.y;
const steer = control.x;

InputActionMap

Method / propertyDescription
FindAction(name)Get action by name (warns if missing)
Enable() / Disable()Toggle whole map (loader enables on Begin)

InputAction (polling)

MethodDescription
ReadValue()Scalar (VECTOR2 → magnitude)
ReadVector2(){ x, y }
IsPressed()True while actuated
WasPressedThisFrame()Edge: just pressed this frame
WasReleasedThisFrame()Edge: just released
WasPerformedThisFrame()Edge: performed callback fired

InputAction (callbacks)

action.started.add(fn), action.performed.add(fn), action.canceled.add(fn) — same semantics as Unity Input System.

Global access (optional)

InputManager.actions · InputManager.FindAction("Player/Move") · InputManager.GetDefaultMap() — prefer @inputMap fields on behaviors.

BehaviorRegistry

Maps script name → class. Apps register before load:

const registry = new BehaviorRegistry();
const modules = import.meta.glob("./behaviors/*.{ts,js}", { eager: true });
AutoRegisterBehaviors(registry, modules);
const level = await new LevelLoader(scene, registry).Load(manifestUrl);

registry.Create("Patrol") is called internally for each SCRIPT row.

App bootstrap (not in behaviors)

Typical main.ts responsibilities — see 02 — Runtime Basics:

  1. FetchAndValidateManifest(url) — optional; required before CreateLevelEngine when using large-world rendering
  2. CreateLevelEngine(canvas, antialias, manifest)
  3. new Scene(engine)
  4. await EnableHavokPhysics(scene, …) — before Load
  5. LevelLoader.Load(manifestUrl, manifest?, options?)
  6. engine.runRenderLoop(() => scene.render())

LevelLoaderOptions

OptionDefaultEffect
shadowstrueCreate shadow generators
shadowMapSize1024Shadow map resolution
freezeShadowsfrom manifestStatic shadow bake
cleanBoneMatrixWeightsfalseScrub imprecise skeleton bone weights on load (fixes garbled shadows on skinned meshes)
debugCollidersfalseWireframes on load (also gated by debug flag)
clusterPunctualLightsfrom manifestLight clustering policy
lightBudget8Max forward punctual lights

Physics (from behaviors)

When an entity has a body: this.entity.body is a Babylon PhysicsBody. Use Havok APIs for impulses, velocities, etc. Collider overlay: app debug key → level.ShowColliders() (when level.debugEnabled).

Messages

Authored Event Messages and 3D GUI buttons call targetEntity.SendMessage(message, sourceEntity). Every behavior on the target gets OnMessage(message, source). Use the same string names you authored in Blender event rows.

Related exports

@bjs/engine also re-exports manifest types (core/types.ts), subsystem helpers (physics, lights, …), and UI loaders — mainly for app code and advanced scripts. Behavior authors usually need Behavior, exposed, inputMap, and Babylon types only.