# followGround

> Talos 0.6 · Since 0.6

Keep this entity on the ground: cast from above its own position, put it on what the cast found and return what was decided (`position` in the world frame).

Call it after moving the entity across the ground this tick. Under a parent, only the height changes, so a parent that is rotated off level gets a body that is lifted along the world's up, not its own. Its memory between ticks (the fall speed, the eased normal) is kept in `ctx.state[key]`, default `"ground"`, as plain data, so it survives a hot reload. `ts update(ctx, dt) { ctx.entity.translate([speed * dt, 0, 0]) const g = followGround(ctx, { radius: 0.5 }) if (!g.grounded) ctx.log("over the edge") } ```

## Signature

```ts
export function followGround(ctx: Ctx<any, any>, opts: FollowGroundOptions = {}, key = "ground"): GroundFollow
```

Import: `import { followGround } from "@argus/gameplay"`

## Example

The tested fixture `crates/editor/templates/behaviours/roll.ts`.

```ts
import type { Behavior, Vec3 } from "@argus/api"
import { followGround, rollRotation, type GroundFollow } from "@argus/gameplay"

/**
 * A ball this script moves: rolls across the ground at a steady speed, stays on it, and spins the
 * way it is going.
 *
 * The two hard parts come from `@argus/gameplay`, the helpers the engine ships, rather than being
 * written again here. `followGround` casts from above the ball's own position, keeps it resting on
 * whatever is under it at its radius, and lets it fall off an edge instead of snapping down.
 * `rollRotation` turns the distance it actually moved into the spin that matches it (`rollFromMotion`
 * is the same thing as an axis and an angle): forward motion, forward spin, one radian per radius
 * travelled. Move across the ground first (x and z), then call `followGround`, then roll by what
 * the two together did.
 *
 * Give the entity no rigidbody: this script is what moves it. A collider on it is fine.
 */
interface State {
  /** `followGround`'s memory between ticks. */
  ground?: GroundFollow
}

interface Props {
  /** Metres per second. Default 2. */
  speed?: number
  /** Direction to roll, degrees about the vertical: 0 is +x, 90 is -z. Default 0. */
  heading?: number
  /** The ball's radius, metres: how high it rests and how fast it spins. Default 0.5. */
  radius?: number
}

export default {
  update(ctx, dt) {
    const speed = typeof ctx.props.speed === "number" ? ctx.props.speed : 2
    const heading = ((typeof ctx.props.heading === "number" ? ctx.props.heading : 0) * Math.PI) / 180
    const radius = typeof ctx.props.radius === "number" ? ctx.props.radius : 0.5
    const before = ctx.entity.worldPosition
    const p = ctx.entity.position
    ctx.entity.position = [p[0] + Math.cos(heading) * speed * dt, p[1], p[2] - Math.sin(heading) * speed * dt]
    const ground = followGround(ctx, { radius })
    const after = ctx.entity.worldPosition
    const moved: Vec3 = [after[0] - before[0], after[1] - before[1], after[2] - before[2]]
    ctx.entity.rotation = rollRotation(ctx.entity.rotation, moved, radius, ground.normal)
  },
} satisfies Behavior<State, Props>
```
