Quat
Since 0.6
Quaternion [x, y, z, w].
Signature
export type Quat = [number, number, number, number]
Import: import type { Quat } from "@argus/api"
Example
The tested fixture crates/editor/templates/behaviours/rotate.ts.
import type { Behavior, Quat } from "@argus/api"
/**
* Spins the entity it is attached to, at a steady rate, about one axis.
*
* The rotation is written as a whole quaternion rather than accumulated onto the one that is
* there: a quaternion multiplied by itself sixty times a second drifts off the unit sphere, and
* an entity whose rotation is no longer a unit quaternion draws sheared. The angle is kept in
* `ctx.state` and the quaternion is built from it each tick, so the value on the entity is
* always exact and a hot reload picks the spin up where it left off.
*
* On a static collider this moves the mesh and not the navmesh, which is built once from the
* scene as authored — so spin decoration, not the floor somebody has to walk on.
*/
interface State {
/** Radians turned so far, wrapped to one turn so it cannot grow without bound. */
angle: number
}
interface Props {
/** Degrees per second. Negative spins the other way. Default 90. */
degreesPerSecond?: number
/** Axis to spin about: `x`, `y` or `z`. Default `y`. */
axis?: "x" | "y" | "z"
}
const TAU = Math.PI * 2
/** A unit quaternion of `angle` radians about one axis, as `[x, y, z, w]`. */
function about(axis: "x" | "y" | "z", angle: number): Quat {
const half = angle / 2
const s = Math.sin(half)
const w = Math.cos(half)
if (axis === "x") return [s, 0, 0, w]
if (axis === "z") return [0, 0, s, w]
return [0, s, 0, w]
}
export default {
init(ctx) {
if (typeof ctx.state.angle !== "number") ctx.state.angle = 0
},
update(ctx, dt) {
const rate = typeof ctx.props.degreesPerSecond === "number" ? ctx.props.degreesPerSecond : 90
ctx.state.angle = (ctx.state.angle + (rate * Math.PI) / 180 * dt) % TAU
ctx.entity.rotation = about(ctx.props.axis ?? "y", ctx.state.angle)
},
} satisfies Behavior<State, Props>