Gessa Docs
Recipes

Recipe

Player movement abilities dash sprint and double jump

The player already walks and jumps (see third-person-and-platformer-movement or first-person-controls) and now needs an extra move: a dash burst, a sprint that speeds them up while a key is held, or a second jump in the air. The engine has no DashComponent, SprintComponent, or DoubleJumpComponent; each ability is a script layered on the real CharacterMovementComponent motor, and the burst itself
engine v1.0.234since component-field-contract ecs.component-field-contract.v1, script-semantic-patch-ops.v1, action-catalog.v1.0.232, physics.v1, input.v1, persistence.v1Copy for LLM

Use this for

extra traversal moves layered on top of basic walk and jump; a dash or dodge burst; a sprint that speeds the player up while held; a double or triple jump; air-dash and momentum moves keyed to an input

Not for

the basic walk-and-jump motor itself (see third-person-and-platformer-movement or first-person-controls); a pad that launches the player on contact (see jump-pads-and-launchers); mapping the raw key to an action (see input-mapping-actions-bindings)

Pairs with: Third-person follow camera and platformer jump movement, First-person camera movement and mouselook controls, Input mapping actions bindings keyboard mouse gamepad touch, Physics colliders rigid bodies triggers and joints, Game feel juice hit flash camera kick and impact

When to use

The player already walks and jumps (see third-person-and-platformer-movement or first-person-controls) and now needs an extra move: a dash burst, a sprint that speeds them up while a key is held, or a second jump in the air. The engine has no DashComponent, SprintComponent, or DoubleJumpComponent; each ability is a script layered on the real CharacterMovementComponent motor, and the burst itself is the same ctx.physics.command launch that jump pads use.

The recipe

Prerequisite: the player carries CharacterMovementComponent (enableKcc: true, a maxSpeed, a jumpSpeed, a groundY) and an InputProfileComponent. Add one action per ability (see input-mapping-actions-bindings): a sprint boolean and a dash (or doubleJump) button. Author the behavior with project_apply_script_semantic_patch using the ai_safe op pack.

Sprint (faster while held)

  1. addActionHandler { "actionKey":"sprint", "handlerKey":"onSprint" }.
  2. In the handler, branch on the InputActionEvent phase: on pressed, ctx.entity.patchComponent the player's CharacterMovementComponent to a higher maxSpeed (for example 11); on released, patch it back to the walk value (for example 7). Read the walk value once with ctx.entity.readComponent and stash it in state if you want it dynamic.
  3. Optional stamina: seed a stamina number with writeTypedStateValue, drain it each tick while sprinting with incrementNumericState { "by": -1 }, and force released behavior when it hits 0.

Dash (a burst in the facing direction)

  1. addActionHandler { "actionKey":"dash", "handlerKey":"onDash" }.
  2. Keep the current facing fresh: addTickHandler { "handlerKey":"onMoveSample" } that, in a replaceScriptFromGessaScript body, reads the move action value (the InputActionEvent value, a vector2d) and, when it is non-zero, writeTypedStateValue a facing vector. This gives the dash a direction even at a standstill (last-moved).
  3. In onDash (whole-body GessaScript), read facing, scale it to a dash speed (for example 18), and fire ctx.physics.command(playerId, { type: "launch", velocity: { x: fx*18, y: 0, z: fz*18 } }) where playerId is the entity the handler runs on (ctx.entity is the self-accessor for reads and patches; pass the player's own id to the command). launch SETS the kinematic character's velocity for this step, which is the one sanctioned way to fling a CharacterMovementComponent body; do not patch TransformComponent position to fake the dash.
  4. Cooldown: setBooleanState { "field":"dashReady", "value": false } on dash, and flip it back after a delay with a TimerComponent (see timers-countdowns-and-waves). Gate the dash on dashReady so it cannot be spammed.

Double jump (a second jump before landing)

  1. Track jumps used: seed jumpsUsed to 0 with writeTypedStateValue.
  2. Ground reset: in an addTickHandler body, ctx.entity.readComponent the TransformComponent position.y and the CharacterMovementComponent groundY; when the player is at (or near) groundY, writeTypedStateValue jumpsUsed back to 0. This is the grounded check without a hidden velocity lane.
  3. Jump action: addActionHandler { "actionKey":"jump", "handlerKey":"onJump" }. readTypedStateValue jumpsUsed; if it is less than the allowed count (2 for a double jump), ctx.physics.command(playerId, { type:"launch", velocity:{ x:0, y: jumpSpeed, z:0 } }) and incrementNumericState jumpsUsed { "by": 1 }. When jumpsUsed reaches the cap, ignore the press.

Pitfalls

  • The launch y for a jump should match the movement jumpSpeed so the double jump feels like the first; read it with ctx.entity.readComponent rather than hardcoding a second number that can drift from the motor.
  • launch REPLACES velocity, it does not add to it: a dash issued mid-fall wipes the fall speed for that step. That is usually what you want for a crisp dash; for a momentum-preserving dash, compose the desired resultant yourself.
  • Do NOT author a VelocityComponent or ForceComponent to move the character; both are hidden internal runtime lanes. Every burst goes through ctx.physics.command, every speed change through CharacterMovementComponent.maxSpeed.
  • Ground the double-jump reset on position vs groundY, not on a landing event you cannot subscribe to; there is no grounded-state flag on the movement component to read.
  • Sprint changes maxSpeed; it does not multiply an existing velocity. If sprint feels like it does nothing, confirm you patched the same CharacterMovementComponent the mover reads and that enableKcc is true.

Verify

  • engine_get_component_schema on CharacterMovementComponent to confirm maxSpeed/jumpSpeed/groundY before patching.
  • project_get_graph_snapshot to confirm the sprint/dash/jump handlers and the ScriptComponent binding exist on the player.
  • simulation_run (qa.run.start) to prove sprint raises speed while held, the dash launches in the move direction, and a second jump fires in the air but a third does not.
Was this helpful?Report an issueContact support

On this page