Talos docs

rollFromMotion

Since 0.6

The spin a sphere of radius needs to roll along displacement without slipping.

Plain text

Only the part of the displacement along the ground counts: the component along normal (default up) is the body rising or sinking, which turns nothing. dt is the time the displacement took, for angularVelocity; omitted, that is zero. ts const r = rollFromMotion([4 * dt, 0, 0], 0.5, dt) // +X at 4 m/s r.axis // [0, 0, -1]: rolling toward +X spins about -Z ctx.entity.rotation = quatMultiply(r.delta, ctx.entity.rotation)

Signature

export function rollFromMotion(displacement: Vec3, radius: number, dt = 0, normal: Vec3 = [0, 1, 0]): Roll

Import: import { rollFromMotion } 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>