Sandbox
The sandbox example in examples/src/examples/sandbox/ is a first-person walk through one level authored in Blender, SAGE_dev-box.blend. In one scene it puts a physics character controller, a GLB level with colliders, doors and buttons and crates adopted from the scene, a key to carry, a four-floor elevator with a floor-selection panel, spatial and level-scoped sound, entity picking, and two input contexts. Pooling, saves, the state machine behavior, and tag queries are not part of it.
Walk to the button and press E to open the sliding door. The key is on the table in the next room; carrying it unlocks the locked buttons and crates. The elevator's platform button opens a panel to pick a floor, and the wall buttons call it to theirs. The last room is full of pushable props.
How each piece is built
| What the sandbox does | How, on 0.10 |
|---|---|
| The first-person player | PlayerEntity spawns at the player_start marker. FirstPersonBehavior builds Havok's PhysicsCharacterController in onMeshLoaded, takes over the free camera the level declared in YAML, and moves from action:moveForward, action:moveRight, and action:jump events. See Input. |
| Doors, buttons, crates, the key, the elevator, and the props | Entity markers in Blender, one entities entry per marker value in sandbox.yaml. Each entity adopts its node. See Authoring. |
| What a button does | Authored on the node as custom properties, target, prompt, locked, and context, read from this.entity.node.metadata in InteractBehavior.onMeshLoaded the first time the node is adopted. |
| The interact prompt | engine.simulation.pickEntityForward(camera, 3) every frame, then hasBehavior(InteractBehavior) or hasBehavior(PickupBehavior) on the hit to read its prompt. See Picking. |
| Pressing E | The controller emits interact:use targeted at the picked entity. InteractBehavior answers by broadcasting interact:activate with its target node's name; DoorBehavior, CrateBehavior, and ElevatorBehavior each answer when the name is their own node's. See Talking to One Entity. |
| Unlocking with the key | InteractBehavior declares an unlock operation. The controller calls it through the narrowed ops when the player is carrying the key, which it tracks from pickup:heldChanged. |
| Door and unlock sounds | SoundBehavior, composed onto DoorEntity, ButtonEntity, and CrateEntity. The door's two sounds are spatial, attached to the adopted node; InteractBehavior.unlock plays through this.ops.play('unlock'). See Audio. |
| The factory ambience | A level-scoped sound in sandbox.yaml, looping on the ambient channel. The HUD's mute button toggles the channel. See Level Sounds. |
| The elevator moving | ElevatorBehavior switches its adopted node's physics body to animated in onMeshLoaded and drives it with setTargetTransform every fixed step, writing travel into state. |
| The player riding the elevator | The elevator broadcasts elevator:moved with the distance it moved that step; FirstPersonBehavior subscribes and carries the capsule the same distance. |
| The floor panel and its input context | panelOpen is a field in the elevator's state, and ElevatorBehavior broadcasts elevator:panelChanged on every transition. The controller answers that event: elevator_ui active and the pointer released while the panel is open, gameplay active and the pointer locked again when it closes. Activating one context deactivates the other, and Escape is bound in elevator_ui only. See Action contexts. |
| Collider visualization | F3, or the HUD button, calls engine.debug.colliders.showColliderForNode for every node with a physics body. See Collider Debugging. |
| The controller hearing actions | SandboxController registers on engine.simulation.bus for action:interact and action:toggleColliders, and for the level's own elevator:panelChanged, elevator:arrived, and pickup:heldChanged, as a subscriber outside the entity system. It pushes the HUD's state from those handlers instead of rebuilding it every frame. See Events. |
The level config
name: Sandbox
scene: /assets/sandbox/SAGE_dev-box.glb
physics: true
cameras:
PlayerCamera:
type: free
attachControl: true
minZ: 0.1
spawns:
player_start:
entity: PlayerEntity
entities:
door:
entity: DoorEntity
button:
entity: ButtonEntity
elevator_button:
entity: ButtonEntity
crate:
entity: CrateEntity
key:
entity: KeyEntity
elevator:
entity: ElevatorEntity
prop:
entity: PropEntity
postProcessing:
renderer: frameGraph
bloom:
weight: 0.3
threshold: 0.8
tonemap:
operator: aces
sounds:
ambient:
url: /assets/sandbox/ambient-factory.mp3
channel: ambient
loop: true
autoplay: true
volume: 0.04The colliders come from the collider custom property on the Blender nodes, so the example registers the built-in property handlers before loading:
registerAllPropertyHandlers(engine.managers.levelManager);Next steps
- Blender Workflow: the custom properties the sandbox scene uses
- Level Loading: spawn and entity markers on 0.10
- Input System: actions, bindings, and contexts
