Recipes
Physics colliders rigid bodies triggers and joints
Anything that blocks movement, detects overlaps, falls under gravity, or is physically constrained to another body. Colliders are passive shapes; rigid bodies are dynamic; joints are constraints.
engine v1.0.234since component-field-contract ecs.component-field-contract.v1, physics.v1Copy for LLM
Use this for
making things solid, bouncy, or dynamic; trigger volumes; dynamic Rapier bodies; hinges, springs, and ropes between bodies
Not for
character walking motion (use CharacterMovementComponent); applying forces from author-time (use ctx.physics.command from a script)
Pairs with: World build document authoring compile apply loop, Third-person follow camera and platformer jump movement, Combat damage health and respawn scripting, Collectibles pickups scoring and win condition
Bodies first, components second
Place the physical entities themselves in bulk with the world build document (see authoring-with-the-world-build-document): the floor, the walls, the props. That lands their Transform and Renderable as one transaction. Physics is a COMPONENT layer attached on top: the document does not emit colliders or rigid bodies, so attach them as a precision step. To make many entities solid or dynamic at once, attach the components in ONE atomic project_apply_transaction rather than one entity per call.
The recipe
- Solid blocker or ground: attach
ColliderComponent{ "type":"ColliderComponent", "shape":"box", "collision":"solid", "size":{"x":10,"y":1,"z":10}, "layers":["default"], "friction":0.5, "restitution":0 }.shapeisbox,sphere,capsule,disc, ormesh. - Trigger volume (fires overlap events, does not block): same component with
"collision":"trigger". A script reacts withaddTriggerHandler(its payload is{firstEntityId, secondEntityId, worldId, distance}). TRIGGER PARTICIPANT RULE (load-bearing, proven on the arena benchmark): the moving entity that should FIRE a static trigger must NOT carry a RigidBodyComponent. Any RigidBody hands that entity's pairs to Rapier, where a kinematic-vs-static-sensor pair is never reported, so the trigger silently never fires (16 entities in, 16 out, zero diagnostics). A player is CharacterMovementComponent with enableKcc plus a collider - no RigidBody, ever. If a trigger seems dead, check the toucher for a stray RigidBodyComponent first and remove it with project_remove_component. - Dynamic object: add
RigidBodyComponent{ "type":"RigidBodyComponent", "bodyType":"dynamic", "mass":1, "gravityScale":1, "linearDamping":0, "angularDamping":0, "lockTranslations":{"x":false,"y":false,"z":false}, "lockRotations":{"x":false,"y":false,"z":false} }.bodyTypeisdynamic,kinematic, orstatic. EnableccdEnabledfor fast movers. - Constrain two bodies: add
JointComponenton the child body withjointTypefixed,revolute,prismatic,spherical,spring, orrope, and setconnectedEntityIdto the other body. Anchors andaxisare in each body's LOCAL space;motorandlimitsapply to revolute and prismatic joints. - Apply an impulse or launch from a script (not from authoring): call
ctx.physics.command(entityId, bodyCommand); query space withctx.physics.overlapTest,ctx.physics.shapecast,ctx.physics.occupancyQuery.
Pitfalls
frictionandrestitutionare clamped 0..1. A bouncy ball is highrestitution; ice is lowfriction.- Do NOT author
ForceComponentorVelocityComponentvalues: both are hidden internal runtime lanes. Forces go throughctx.physics.command; the movement vector is runtime-owned. - A
RigidBodyComponentmakes the runtime own that entity's Transform each tick. Do not also drive it withCharacterMovementComponent; pick one motor. - A collider with
collision:"none"neither blocks nor triggers; it is only a query/placement shape. - Joint
motor/limitson afixedjoint do nothing; they exist only on revolute and prismatic. - The document places the bodies; the per mutation call is the precision tier for attaching these components, not a bulk placement tool.
Verify
engine_get_component_schemaonColliderComponentandRigidBodyComponentfor shape/bodyType enums.simulation_run(qa.run.start) to confirm solids block and triggers fire.runtime_proof_run(runtime.proof.run) for a deterministic physics transition receipt.