Wheel
Present on ctx.wheel while the entity is a running wheel: a wheel component on an entity with a dynamic body above it in the hierarchy.
Null otherwise. Also reached as ctx.body.wheels and ctx.vehicle.wheels from the body's own behaviour. Who drives. A wheel no vehicle composes is driven by behaviours: {@link Wheel.setMotorTorque}, {@link Wheel.setBrakeTorque} and {@link Wheel.setSteer} hold until the next call, and each tick the wheel's spring and tyre push its body — a cart, a trailer, a robot on wheels. A wheel a vehicle composes is driven by the vehicle. On an input: "script" vehicle, each field a behaviour writes takes that wheel over from the vehicle's command until the vehicle's next {@link Vehicle.drive} (torque vectoring, a spun-up wheel); on an auto vehicle a write changes nothing and is counted on GET /perf → unwired.vehicle_ignored. Signs. Positive motor torque rolls it along its forward (its composed -Z). Steer is positive right, like the steer action and {@link Vehicle.steerAngle}. ts // A cart: drive every wheel, steer the ones named "front-…", and brake when nothing is held. update(ctx) { const go = ctx.input?.action("throttle").held ?? false for (const w of ctx.body!.wheels) { w.setMotorTorque(go ? 40 : 0) w.setBrakeTorque(go ? 0 : 200) if (w.entity.id.startsWith("front-")) w.setSteer(0.3 * (ctx.input?.axis("steer")[0] ?? 0)) } }
Signature
export interface Wheel {
/** Drive torque at the axle, N·m, from the next tick until the next call. Positive rolls it forward. */
setMotorTorque(nm: number): void
/** Brake torque, N·m, 0 or more, from the next tick until the next call. */
setBrakeTorque(nm: number): void
/** Steer angle about the body's up, radians, **positive right**, from the next tick until the next call. */
setSteer(radians: number): void
/** The wheel's own entity. */
readonly entity: EntityHandle
/** `"script"`: behaviours drive it. `"vehicle"`: its vehicle does. */
readonly drivenBy: "script" | "vehicle"
readonly grounded: boolean
/** Where it touches the ground; null in the air. */
readonly contact: WheelContact | null
/** Slip ratio (`long`; 0 on a `clamp` tyre, which rolls at road speed) and slip angle (`lat`), radians. */
readonly slip: { long: number; lat: number }
readonly rpm: number
/** N the spring carries. */
readonly load: number
/** 0 at the spring's rest length or longer, 1 against the bump stop. */
readonly compression: number
/** Radians, **positive right**. */
readonly steerAngle: number
/** N·m: the torques in force — a behaviour's, or what its vehicle asked of it. */
readonly motorTorque: number
readonly brakeTorque: number
/** Metres. */
readonly radius: number
}
Import: import type { Wheel } from "@argus/api"
Members
| Member | Signature | Description |
|---|---|---|
setMotorTorque | setMotorTorque(nm: number): void | Drive torque at the axle, N·m, from the next tick until the next call. Positive rolls it forward. |
setBrakeTorque | setBrakeTorque(nm: number): void | Brake torque, N·m, 0 or more, from the next tick until the next call. |
setSteer | setSteer(radians: number): void | Steer angle about the body's up, radians, positive right, from the next tick until the next call. |
entity | readonly entity: EntityHandle | The wheel's own entity. |
drivenBy | readonly drivenBy: "script" | "vehicle" | "script": behaviours drive it. "vehicle": its vehicle does. |
grounded | readonly grounded: boolean | |
contact | readonly contact: WheelContact | null | Where it touches the ground; null in the air. |
slip | readonly slip: { long: number; lat: number } | Slip ratio (long; 0 on a clamp tyre, which rolls at road speed) and slip angle (lat), radians. |
rpm | readonly rpm: number | |
load | readonly load: number | N the spring carries. |
compression | readonly compression: number | 0 at the spring's rest length or longer, 1 against the bump stop. |
steerAngle | readonly steerAngle: number | Radians, positive right. |
motorTorque | readonly motorTorque: number | N·m: the torques in force — a behaviour's, or what its vehicle asked of it. |
brakeTorque | readonly brakeTorque: number | |
radius | readonly radius: number | Metres. |
Example
The tested fixture fixtures/recipes/collectible-and-goal/scenes/behaviors/collectible.ts.
import type { Behavior } from "@argus/api"
/**
* A coin the player collects by walking into it.
*
* It says so once, as `collected`, and leaves the world. Counting is somebody else's job —
* `goal-exit.ts` — so a coin never needs to know how many there are.
*
* The contact is `onTrigger`, which needs three things in the scene: a collider with
* `isTrigger: true` (the volume), a `trigger` component (what fires), and a body on the player —
* the character controller's capsule is one.
*/
interface State {
collected: boolean
}
interface Props {
/** Tag the toucher must carry. Default `player`. */
playerTag?: string
}
export default {
init(ctx) {
if (typeof ctx.state.collected !== "boolean") ctx.state.collected = false
},
onTrigger(ctx, trigger) {
if (ctx.state.collected || !trigger.entering) return
if (!trigger.other.hasTag(ctx.props.playerTag ?? "player")) return
ctx.state.collected = true
// The `goal` component on this coin has the same id, so the playtester and the HUD agree.
ctx.events.emit("collected", { goal: ctx.entity.id })
// Last: after `despawn` every member of this entity's handles throws.
ctx.entity.despawn()
},
} satisfies Behavior<State, Props>