GroundFollow
Since 0.6
What one tick of ground-following decided.
Plain data: it round-trips through ctx.state.
Signature
export interface GroundFollow {
/** Where the body is now. */
position: Vec3
/** On the ground this tick. */
grounded: boolean
/** The ground's normal under the body, eased by `smoothing`; up while airborne. */
normal: Vec3
/** Vertical speed, m/s: 0 on the ground, negative while falling. */
vy: number
/** The ground rose by more than `maxStep` this tick; the body was lifted onto it anyway. */
blocked: boolean
/** The entity it stands on, or null. */
ground: string | null
}
Import: import type { GroundFollow } from "@argus/gameplay"
Members
| Member | Signature | Description |
|---|---|---|
position | position: Vec3 | Where the body is now. |
grounded | grounded: boolean | On the ground this tick. |
normal | normal: Vec3 | The ground's normal under the body, eased by smoothing; up while airborne. |
vy | vy: number | Vertical speed, m/s: 0 on the ground, negative while falling. |
blocked | blocked: boolean | The ground rose by more than maxStep this tick; the body was lifted onto it anyway. |
ground | ground: string | null | The entity it stands on, or null. |
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>