Gessa Docs
Recipes

Recipe

Moving platforms, elevators, rotating hazards, and objects on a patrol path

A platform slides side to side, a lift rises and falls, or a blade spins. There is no MotionComponent (stripped); self-driven motion is a tick handler that patches the TransformComponent every frame.
engine v1.0.234since script-semantic-patch-ops.v1, action-catalog.v1.0.232, mutation.v1, hierarchy.v1, timer.v1Copy for LLM

Use this for

a platform that slides back and forth or up and down; elevators and lifts; rotating blades, gears, or swinging obstacles; any object that moves on its own path without player input

Not for

objects that move only when interacted with (see doors-buttons-and-interactions); projectiles that fly and hit (see projectiles-and-ranged-weapons); the player's own movement (see third-person-and-platformer-movement)

Pairs with: Doors buttons levers and press-to-interact, Physics colliders rigid bodies triggers and joints, Hazards lava damage zones and water, Timers countdowns cooldowns and enemy waves

The recipe

Author with project_apply_script_semantic_patch using the ai_safe op pack. The mover is an entity with a TransformComponent and usually a solid ColliderComponent so the player can stand on or be blocked by it.

  1. Add a tick handler: addTickHandler { "handlerKey":"onMove" }.
  2. In the body (whole-body GessaScript via replaceScriptFromGessaScript), read the current transform with ctx.entity.readComponent (TransformComponent), compute the next position, and write it with ctx.entity.patchComponent. Scale every delta by ctx.delta (seconds since last tick) so speed does not depend on frame rate.
  3. Oscillate back and forth: keep a phase in durable state, advance it each tick with incrementNumericState { "field":"phase", "by": <speed * delta> }, and map it to an offset (a triangle or sine of the phase). Or ping-pong: track a goingUp flag with setBooleanState, move toward the current endpoint, and flip the flag when you pass it.
  4. Rotate a blade or gear: patch rotation instead of position, adding a per-tick angle. Parent the visible mesh under a pivot entity with ctx.entity.setParent when the spin axis is not the mesh center.
  5. Timed lift (moves only on an interval): drive it from a TimerComponent plus addTimerHandler instead of every tick, patching to the next stop each fire.

Pitfalls

  • Multiply every movement by ctx.delta. A fixed per-tick step drifts when the tick rate changes and desyncs between clients.
  • Patch position/rotation through ctx.entity.patchComponent; do NOT write VelocityComponent (it is engine-internal and hidden from AI/SDK authoring).
  • Transform patches are server-authoritative (hierarchy.server_authority), so the platform moves identically for all players; ride-along works because the player stands on a solid collider.
  • Store oscillation phase or the ping-pong flag in durable state, not a local variable, so the platform keeps its cycle across a reload.
  • Carry the rider by making the platform solid and letting the physics step resolve standing contact; do not teleport the player each tick.

Verify

  • simulation_run (qa.run.start) to watch the platform complete a full cycle and confirm a standing entity is carried and a solid mover blocks passage.
  • project_get_graph_snapshot to confirm the tick or timer handler landed.
Was this helpful?Report an issueContact support

On this page