Talos docs

followGround

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).

Plain text

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

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.

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>