---
title: "Physics"
description: "Physics namespace: collisions, joints, raycasts, body manipulation."
section: "API Reference"
slug: "api-physics-2"
canonical: "https://origozero.ai/docs/api-physics-2"
updated: "2026-10-20T11:00:02.781469233+00:00"
tags: ["api", "reference"]
---

# Physics

Physics namespace — collisions, joints, raycasts, body manipulation. Auto-injected by the prelude from `@builtin::modules.api.engine.physics`. Same table as the lowercase `physics` alias.

```lua
Physics: any
```

The `Physics` namespace: 80 functions.

## globals/Physics/COLLIDER_COMPONENTS {#globals-physics-collider-components}

```lua
Physics.COLLIDER_COMPONENTS()
```

Every collider component, in the order a lookup walks them. Shape is the
component's identity, so code that works on "whatever collider this entity
has" walks this list rather than guessing a shape.

```lua
for _, name in ipairs(Physics.COLLIDER_COMPONENTS) do ... end
```

## globals/Physics/addCollider {#globals-physics-addcollider}

```lua
Physics.addCollider(entityId: string | entityRef, component: string, config: table?)
```

Add a collider component to an entity, naming the shape you want.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `component` `string` — One of `Physics.COLLIDER_COMPONENTS`.
- `config` `table` _(optional)_ — The component's own fields, e.g. `{ radius = 0.5 }` for a sphere.

```lua
Physics.addCollider(id, "SphereCollider", { radius = 0.5 })
```

## globals/Physics/addConstraint {#globals-physics-addconstraint}

```lua
Physics.addConstraint(entityId: string | entityRef, opts: table?)
```

Add a transform constraint to an entity.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `opts` `table` _(optional)_ — Optional constraint description (targetEntityId, position, rotation, scale, lookAt, targetPosition, axes, weight).

```lua
Physics.addConstraint(id, { targetEntityId = parent, position = true })
```

## globals/Physics/addJoint {#globals-physics-addjoint}

```lua
Physics.addJoint(entityIdA: string | entityRef, entityIdB: string | entityRef, opts: table?)
```

Add a Joint component connecting two entities. Accepts either
vec3-style anchor inputs (`localAnchor = {x,y,z}`) or pre-split
scalar keys (`localAnchorX/Y/Z`).

**Parameters**

- `entityIdA` `string | entityRef` — Entity that hosts the Joint component.
- `entityIdB` `string | entityRef` — Connected entity.
- `opts` `table` _(optional)_ — Optional joint description (kind, anchors, axis, stiffness, damping, restLength, maxDistance, breakForce, breakTorque, region, remoteRegion).
Either end may be a soft body (an entity with a `SoftBody` component):
`region` anchors this end on a region of entity A's soft body and
`remoteRegion` the other end on a region of B's, each as
`Physics.softRegion` takes it (`{ box = { min, max } }` or
`{ nearest = point }`); an end with no region is anchored on the soft
body as a whole. The anchor of a region end is measured from the centre
of its particles.

```lua
Physics.addJoint(a, b, { kind = "fixed" })
Physics.addJoint(a, b, { kind = "hinge", axis = {x=0,y=1,z=0} })
Physics.addJoint(a, b, { kind = "rope", maxDistance = 8 })
Physics.addJoint(a, b, { kind = "fixed", breakForce = 1200, breakTorque = 800 })
Physics.addJoint(bag, hook, { kind = "ball", region = { nearest = {0, 0.5, 0} } })
Physics.addJoint(jellyA, jellyB, { kind = "fixed", region = { box = { min = {0.4, -0.5, -0.5}, max = {0.5, 0.5, 0.5} } }, remoteRegion = { box = { min = {-0.5, -0.5, -0.5}, max = {-0.4, 0.5, 0.5} } } })
```

## globals/Physics/addVelocity {#globals-physics-addvelocity}

```lua
Physics.addVelocity(a: string | entityRef | number | vec3, b: (number | vec3)?, c: number?, d: number?)
```

Add to the linear velocity of an entity. Same call shapes as
`setVelocity`.

**Parameters**

- `a` `string | entityRef | number | vec3` — dx, a `{x, y, z}` delta vector, or an entity id (explicit target).
- `b` `(number | vec3)` _(optional)_ — dy, dx, or the delta vector depending on call form.
- `c` `number` _(optional)_ — dz or dy depending on call form.
- `d` `number` _(optional)_ — Optional dz when targeting an explicit entity.

```lua
Physics.addVelocity(0, 5, 0)
Physics.addVelocity(entityId, 0, 5, 0)
Physics.addVelocity(entityId, {x=0, y=5, z=0})
```

## globals/Physics/addWheelCollider {#globals-physics-addwheelcollider}

```lua
Physics.addWheelCollider(entityId: string | entityRef, config: table?)
```

Add a WheelCollider to an entity. The entity must be a child
(or descendant) of a rigid body — the system walks up the
hierarchy to find the Physics component. Every wheel under one body is
a wheel of that body's ray-cast vehicle.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `config` `table` _(optional)_ — Optional wheel configuration (`radius?`, `suspensionDistance?`, `springRate?`, `damperRate?`, `targetPosition?`, `maxSuspensionForce?`, `motorTorque?`, `brakeTorque?`, `steerAngle?`, `forwardFriction?`, `sidewaysFriction?`, `frictionSlip?`, `sideFrictionStiffness?`, `rollingResistance?`, `width?`, `is2D?`, `visual?`).

```lua
Physics.addWheelCollider(id, { radius = 0.35, motorTorque = 500 })
```

## globals/Physics/applyForce {#globals-physics-applyforce}

```lua
Physics.applyForce(entityIdOrForce: string | entityRef | vec3, force: vec3?, region: PhysicsSoftRegion?)
```

Apply a force to an entity's rigid or soft body for the next physics
step — call every frame for continuous thrust. With one argument the
script-context entity is targeted; with two args the explicit entity
id wins. A soft body takes the force spread over its particles by mass,
so it accelerates as a whole the way a rigid body of its mass would;
`region` puts it on part of the body instead (`{ box = { min, max } }`
or `{ nearest = point }`, as `Physics.softRegion` takes it), which
accelerates that part alike and leaves the rest to follow.

**Parameters**

- `entityIdOrForce` `string | entityRef | vec3` — Entity id (when paired with `force`) OR a force vector for the script-context entity.
- `force` `vec3` _(optional)_ — Optional force vector when targeting an explicit entity.
- `region` `PhysicsSoftRegion` _(optional)_ — Part of a soft body to push, as `Physics.softRegion` takes it; omit for the whole body.

```lua
Physics.applyForce({x=0, y=10, z=0})
Physics.applyForce(entityId, {x=0, y=10, z=0})
Physics.applyForce(cloth, {x=0, y=0, z=4}, { box = { min = {-0.5, -1, 0.4}, max = {0.5, 1, 0.5} } })
```

## globals/Physics/applyForceAtPoint {#globals-physics-applyforceatpoint}

```lua
Physics.applyForceAtPoint(entityId: string | entityRef, force: vec3, point: vec3, opts: PhysicsPushOptions?)
```

Apply a force at a specific world-space point for the next physics
step. On a rigid body it generates the matching torque from the lever
arm. On a soft body it pushes the particles around the point, weighted
from the whole at the point to nothing at `opts.radius` (by default
twice the spacing of the body's particles), so the body gives way there
first. It always reaches at least the four particles nearest the point,
the smallest patch a body can dent, so a radius finer than the particles
are spaced spreads over that patch.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `force` `vec3` — Force vector, in newtons.
- `point` `vec3` — World-space application point.
- `opts` `PhysicsPushOptions` _(optional)_ — `{ radius: number? }`: how far into a soft body the force reaches, in metres.

```lua
Physics.applyForceAtPoint(id, {x=0,y=10,z=0}, {x=1,y=0,z=0})
Physics.applyForceAtPoint(jelly, {x=0,y=-40,z=0}, {x=0,y=1.5,z=0}, { radius = 0.3 })
```

## globals/Physics/applyImpulse {#globals-physics-applyimpulse}

```lua
Physics.applyImpulse(entityIdOrImpulse: string | entityRef | vec3, impulse: vec3?, region: PhysicsSoftRegion?)
```

Apply an instantaneous impulse (one-shot velocity change). With
one argument the script-context entity is targeted; with two args
the explicit entity id wins. A soft body takes the impulse spread over
its particles by mass, so it moves off as a whole the way a rigid body
of its mass would; `region` puts it on part of the body instead (as
`Physics.softRegion` takes it), which sets that part moving and leaves
the rest to follow. `applyImpulseAtPoint` aims one at a point.

**Parameters**

- `entityIdOrImpulse` `string | entityRef | vec3` — Entity id (with `impulse`) OR an impulse vector for the script-context entity.
- `impulse` `vec3` _(optional)_ — Optional impulse vector when targeting an explicit entity.
- `region` `PhysicsSoftRegion` _(optional)_ — Part of a soft body to push, as `Physics.softRegion` takes it; omit for the whole body.

```lua
Physics.applyImpulse({x=0, y=5, z=0})
Physics.applyImpulse(entityId, {x=0, y=5, z=0})
Physics.applyImpulse(jelly, {x=0, y=-2, z=0}, { box = { min = {-0.5, 0.4, -0.5}, max = {0.5, 0.5, 0.5} } })
```

## globals/Physics/applyImpulseAtPoint {#globals-physics-applyimpulseatpoint}

```lua
Physics.applyImpulseAtPoint(entityId: string | entityRef, impulse: vec3, point: vec3, opts: PhysicsPushOptions?)
```

Apply an instantaneous impulse at a world-space point: a hit, a kick,
a crash. On a rigid body it changes the body's velocity and spin as an
impulse at that point of it would. On a soft body it pushes the particles
around the point, weighted from the whole at the point to nothing at
`opts.radius` (by default twice the spacing of the body's particles), so
the body dents there and the rest of it is carried along through its
elastic elements. It always reaches at least the four particles nearest
the point, the smallest patch a body can dent, so a radius finer than
the particles are spaced spreads over that patch; a finer dent takes a
finer body (a higher `resolution`). The impulse is the momentum the body
gains, in newton-seconds, wherever it lands.

**Parameters**

- `entityId` `string | entityRef` — The body's entity id or proxy.
- `impulse` `vec3` — The impulse, in newton-seconds.
- `point` `vec3` — Where it lands, in world space: a contact point.
- `opts` `PhysicsPushOptions` _(optional)_ — `{ radius: number? }`: how far into a soft body the impulse reaches, in metres.

```lua
Physics.applyImpulseAtPoint(crate, {x=0, y=0, z=-30}, hit.point)
local contact = Physics.contacts(shell)[1]; if contact then Physics.applyImpulseAtPoint(shell, {x=-60, y=0, z=0}, contact.points[1].point, { radius = 0.4 }) end
```

## globals/Physics/applyRadialImpulse {#globals-physics-applyradialimpulse}

```lua
Physics.applyRadialImpulse(entityId: string | entityRef, center: vec3, magnitude: number, opts: PhysicsPushOptions?)
```

Push a body straight away from a world point, like a blast: the
push is `magnitude` newton-seconds at the centre, weighted to nothing at
`opts.radius` (omitted, it reaches any distance at full strength). A
rigid body is pushed by the distance to its centre of mass; a soft body
particle by particle, each away from the centre by its own distance, so
a blast beside a balloon pushes it off and one inside a body spreads it
out. Each moves off at the speed `magnitude` gives a body of its mass.

**Parameters**

- `entityId` `string | entityRef` — The body's entity id or proxy.
- `center` `vec3` — The blast's world position.
- `magnitude` `number` — The impulse at the centre, in newton-seconds.
- `opts` `PhysicsPushOptions` _(optional)_ — `{ radius: number? }`: how far the push reaches, in metres.

```lua
Physics.applyRadialImpulse(balloon, {x=0, y=1, z=-1}, 8, { radius = 3 })
for _, id in pile do Physics.applyRadialImpulse(id, blast, 20, { radius = 4 }) end
```

## globals/Physics/applyTorque {#globals-physics-applytorque}

```lua
Physics.applyTorque(entityIdOrTorque: string | entityRef | vec3, torque: vec3?)
```

Apply a torque to an entity's rigid body for the next physics
step — call every frame for continuous spin-up. With one argument
the script-context entity is targeted; with two args the explicit
entity id wins.

**Parameters**

- `entityIdOrTorque` `string | entityRef | vec3` — Entity id (with `torque`) OR a torque vector for the script-context entity.
- `torque` `vec3` _(optional)_ — Optional torque vector when targeting an explicit entity.

```lua
Physics.applyTorque({x=0, y=1, z=0})
Physics.applyTorque(entityId, {x=0, y=1, z=0})
```

## globals/Physics/attachSoftRegion {#globals-physics-attachsoftregion}

```lua
Physics.attachSoftRegion(softId: string | entityRef, targetId: string | entityRef, region: PhysicsSoftRegion?) -> number
```

Hold a region of a soft body to a rigid body, two-way: the region
follows the rigid body wherever it goes, and the rigid body carries the
region's weight and pull. Each particle in the region is tied to the
point of the rigid body it stands at when the attachment is made. The
attachment is an entry of the soft body's `SoftBody` component
(`attachments`), so it is saved with the scene, reaches every peer, and is
made again on a rebuilt body; it is released when either side is
removed. Its load reads back from `Physics.softBodyState(softId).attachments`.
A cloth's edge on a flagpole, a soft panel on a car's chassis, a jelly
glued to a moving platform. Where the rigid body's colliders touch the
soft body (a shell resting on its chassis), the contact and the hold push
on each other: keep them clear, or `Physics.ignoreCollision(softId,
targetId)`.

**Parameters**

- `softId` `string | entityRef` — The soft body's entity (it carries a `SoftBody` component).
- `targetId` `string | entityRef` — The rigid body's entity (it carries `Physics`), by id, name or proxy.
- `region` `PhysicsSoftRegion` _(optional)_ — Which particles to hold, as `Physics.softRegion` takes it: `{ box = { min, max } }`, `{ nearest = point }`, with `space = "world"` for world coordinates; nil holds the whole body.

**Returns** `number` — The attachment's index in the component's `attachments`.

```lua
Physics.attachSoftRegion(flag, pole, { box = { min = {-0.5, -1, -1}, max = {-0.45, 1, 1} } })
Physics.attachSoftRegion(shell, chassis, { box = { min = {-0.5, -0.5, -0.5}, max = {0.5, -0.4, 0.5} } })
```

## globals/Physics/bodyState {#globals-physics-bodystate}

```lua
Physics.bodyState(entityId: string | entityRef) -> PhysicsBodyState?
```

Everything the solver holds for one body — its type, mass, centre of
mass, inertia, gravity scale, damping, lock flags, CCD, collision groups,
sleep state, velocities, the force and torque queued for the next step,
its colliders, contacts, joints and transform constraints, and why it is
not moving.

**Parameters**

- `entityId` `string | entityRef` — Entity id or proxy.

**Returns** `PhysicsBodyState?` — A `PhysicsBodyState` (with `exists = false` for an entity that carries no rigid body, and `waitingFor` naming what its collider needs while that collider cannot be built), or `nil` when nothing in the scene answers to that id.

```lua
local b = Physics.bodyState(id); print(b.bodyType, b.mass, b.stillness)
local b = Physics.bodyState(id); if not b.exists then print(b.waitingFor or "no body was built") end
```

## globals/Physics/boxCast {#globals-physics-boxcast}

```lua
Physics.boxCast(origin: vec3, halfExtents: vec3, direction: vec3, maxDistance: number?, exclude: (string | {string})?) -> table?
```

Cast a box along a direction and return the first hit.
A trigger (`isTrigger`) holds nothing up, so the sweep passes through it.

**Parameters**

- `origin` `vec3` — Box center at the start of the cast.
- `halfExtents` `vec3` — Half the size of the box on each axis.
- `direction` `vec3` — Cast direction, of any length: the engine normalises it, so
`maxDistance` and `distance` are world units.
- `maxDistance` `number` _(optional)_ — Optional distance limit.
- `exclude` `(string | {string})` _(optional)_ — Optional entity id, or array of entity ids, to exclude from hits.
Applied while sweeping rather than to the answer, so a cast that starts
inside an excluded collider reports what is behind it.

**Returns** `table?` — Hit table `{ entityId, point, normal, distance, startedInside, softBody? }`, or `nil` if nothing hit. `startedInside` is `false` for a surface the query crossed on its way there, and `true` when the query's own start already lay inside that collider — where `distance` is 0, `point` is the start itself, and `normal` is the shortest way out of the collider. `normal` is a unit vector either way. `softBody` is set when what was hit is a soft body, whose surface the query meets as it is deformed now: a `PhysicsSoftHit` naming the surface triangle, the particles carrying it with the hit's weight on each, the particle nearest the hit, and `region`, the hit as a soft region to hand to `Physics.applyImpulse`, `Physics.pinSoftRegion` or `Physics.softParticles`. `Physics.applyImpulseAtPoint` takes the hit's `point` as it is.

```lua
local hit = Physics.boxCast(o, {x=0.5,y=0.5,z=0.5}, dir, 10)
local hit = Physics.boxCast(o, {x=0.5,y=0.5,z=0.5}, dir, 10, { selfId, carriedId })
```

## globals/Physics/capsuleCast {#globals-physics-capsulecast}

```lua
Physics.capsuleCast(origin: vec3, radius: number, halfHeight: number, direction: vec3, maxDistance: number?, exclude: (string | {string})?) -> table?
```

Cast an upright capsule along a direction and return the first hit.
This is the sweep that answers whether a body of that shape fits through
a passage: a capsule of radius `r` reports a hit on anything that leaves
it less than `2 * r` of clearance.
A trigger (`isTrigger`) holds nothing up, so the sweep passes through it.

**Parameters**

- `origin` `vec3` — Capsule centre at the start of the cast.
- `radius` `number` — Capsule radius.
- `halfHeight` `number` — Distance from the centre to either cap centre. The capsule
stands `halfHeight + radius` tall in each direction.
- `direction` `vec3` — Cast direction, of any length: the engine normalises it, so
`maxDistance` and `distance` are world units.
- `maxDistance` `number` _(optional)_ — Optional distance limit.
- `exclude` `(string | {string})` _(optional)_ — Optional entity id, or array of entity ids, to exclude from hits.
Applied while sweeping rather than to the answer, so a cast that starts
inside an excluded collider reports what is behind it.

**Returns** `table?` — Hit table `{ entityId, point, normal, distance, startedInside, softBody? }`, or `nil` if nothing hit. `startedInside` is `false` for a surface the query crossed on its way there, and `true` when the query's own start already lay inside that collider — where `distance` is 0, `point` is the start itself, and `normal` is the shortest way out of the collider. `normal` is a unit vector either way. `softBody` is set when what was hit is a soft body, whose surface the query meets as it is deformed now: a `PhysicsSoftHit` naming the surface triangle, the particles carrying it with the hit's weight on each, the particle nearest the hit, and `region`, the hit as a soft region to hand to `Physics.applyImpulse`, `Physics.pinSoftRegion` or `Physics.softParticles`. `Physics.applyImpulseAtPoint` takes the hit's `point` as it is.

```lua
local hit = Physics.capsuleCast(o, 0.3, 0.6, dir, 0.5)
local hit = Physics.capsuleCast(o, 0.3, 0.6, dir, 0.5, selfId)
```

## globals/Physics/colliderCount {#globals-physics-collidercount}

```lua
Physics.colliderCount() -> number
```

How many colliders the physics world holds. Zero means no ray, cast or
overlap fired into this world can hit anything, so it is what separates a
query that MISSED from a query fired into a world that holds nothing to
hit. Read off the collider set itself, so it costs the same whatever the
world holds.

**Returns** `number` — colliders across the whole physics world.

```lua
if Physics.colliderCount() == 0 then print("nothing here is solid") end
```

## globals/Physics/colliderGeometry {#globals-physics-collidergeometry}

```lua
Physics.colliderGeometry(options: table?) -> table?
```

Read the physics world as drawable triangles: every collider
triangulated in world space into one indexed mesh, in GPU buffers ready to
draw.

Box, sphere, capsule, cylinder, cone, convex, triangle-mesh and heightfield
colliders return their real surface, and a compound returns its children
folded together; a shape with no triangulation returns its bounding box and
reports `exact = false`.

`options.colors` is POSITIONAL over `colliderManifest()` — entry `i` colours
collider `i` — so you can colour by role, shape, entity or anything else you
read there. A position you leave out takes `options.defaultColor`.

The returned buffers are yours: destroy them when you replace them.

**Parameters**

- `options` `table` _(optional)_ — `{ tessellation = "low"|"medium"|"high", colors = { {r,g,b,a}, ... }, defaultColor = {r,g,b,a} }`.

**Returns** `table?` — `{ vertices, indices, vertexCount, indexCount, colliders }` where each entry of `colliders` is `{ entity, colliderName?, shapeType, role, exact, firstIndex, indexCount }`.

```lua
local geo = Physics.colliderGeometry({ tessellation = "high" })
```

## globals/Physics/colliderManifest {#globals-physics-collidermanifest}

```lua
Physics.colliderManifest() -> table
```

List every physics collider in the world with what it is and what it
takes part in — no geometry, so it is the cheap read to make before
deciding what to do with each one.

`role` is one of `static`, `dynamic`, `kinematic`, `sensor`. A sensor
is a collider the simulation holds as one, reported ahead of the body type
behind it, and a collider with no rigid body is static. `exact` says whether `colliderGeometry` would return this
collider's true surface or its bounding box.

Every collider of one entity shares its `entity`, so this is what to key
per-object decisions on. The order is stable across calls over an unchanged
world, which is what makes `colliderGeometry`'s positional colours usable.

**Returns** `table` — Array of `{ entity, colliderName?, shapeType, role, exact }`.

```lua
for _, c in ipairs(Physics.colliderManifest()) do print(c.entity, c.role) end
```

## globals/Physics/colliderOn {#globals-physics-collideron}

```lua
Physics.colliderOn(entityId: string | entityRef) -> string?
```

Which collider component an entity carries, or nil when it carries none.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.

**Returns** string? The component name, e.g. "SphereCollider".

```lua
local which = Physics.colliderOn(id)
```

## globals/Physics/colliderShapes {#globals-physics-collidershapes}

```lua
Physics.colliderShapes(entityId: string | entityRef) -> table
```

Read an entity's resolved physics collider shape(s) as the physics
engine sees them, including auto-sized colliders.

`shapeType` is one of `box`, `sphere`, `capsule`, `convex`, `mesh`,
`heightfield`, `compound`, `other` — the shape the simulation is
running, so a mesh collider reads `mesh`.

`params` carries half-extents for a box, radius for a sphere, radius
and half-height for a capsule, and the collider's bounding half-extents
for the shapes that have no parametric description. A convex collider
reports its outline in `linePoints` instead.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.

**Returns** `table` — Array of resolved collider shapes (empty if none): `{ shapeType, position, rotation, params, linePoints, name? }`.

```lua
local shapes = Physics.colliderShapes(id)
```

## globals/Physics/contacts {#globals-physics-contacts}

```lua
Physics.contacts(entityId: string | entityRef, otherId: (string | entityRef)?) -> { PhysicsContact }
```

Every contact one body's colliders are in right now, with the other
entity, the normal, how deeply the two interpenetrate, the impulse the
last step applied, and each contact point. A pair is listed while its
surfaces touch: a pair kept apart (`Physics.ignoreCollision`, collision
groups) or no longer touching is not. For a soft body these are the
contacts of its surface with every other collider, another soft body's
surface included. A contact with a soft body on either side names the
particles of it that are touching (`particles` for this side,
`otherParticles` for the other), so a hit can be answered where it
landed. Name `otherId` to read only the contacts with that one entity:
a body resting on the ground holds a contact point per surface element
that touches it, and a read of one pair costs that pair alone.

**Parameters**

- `entityId` `string | entityRef` — Entity id or proxy.
- `otherId` `(string | entityRef)` _(optional)_ — Optional entity id or proxy: report only the contacts with it.

**Returns** `{ PhysicsContact }` — An array of `PhysicsContact` — empty when the body touches nothing, or when the entity carries neither a rigid body nor a soft body.

```lua
for _, c in Physics.contacts(id) do print(c.other, c.deepestPenetration) end
for _, c in Physics.contacts(truck, pole) do if c.totalImpulse > 5 then print("dented at", #c.particles, "particles") end end
```

## globals/Physics/cutSoftBody {#globals-physics-cutsoftbody}

```lua
Physics.cutSoftBody(softId: string | entityRef, blade: { vec3 } | { point: vec3, normal: vec3 })
```

Cut a soft body along a blade, every element of the body the blade
meets opening along it: `{ point = p, normal = n }` cuts along the whole
plane through `p` facing `n`, splitting the body in two wherever it lies on
both sides; `{ a, b, c }` cuts along that world-space triangle, which cuts
only as far as the triangle reaches. Applied straight after the next physics
step; a piece the cut separates becomes an entity of its own (see
`onSoftBodyTear`), and the cut reports as a tear of kind "cut". A plane is
measured against the body where that step leaves it, so it cuts a moving
body wherever it has gone; a cut that opens nothing is reported in the log.
Raises when the entity carries no `SoftBody`, and in edit mode, where
nothing simulates.

**Parameters**

- `softId` `string | entityRef` — The soft body's entity.
- `blade` `{ vec3 } | { point: vec3, normal: vec3 }` — `{ point = vec3, normal = vec3 }` for a plane, or the triangle's three world-space corners `{ a, b, c }`.

```lua
Physics.cutSoftBody(jelly, { point = vector.create(0, 1, 0), normal = vector.create(1, 0, 0) })   -- in two along x = 0
Physics.cutSoftBody(jelly, { vector.create(0, -5, -5), vector.create(0, 5, -5), vector.create(0, 0, 10) })
-- slice off the part of a body 0.2 m above its centre
local c = Physics.softBodyState(jelly).centerOfMass
Physics.cutSoftBody(jelly, { point = vector.create(c.x, c.y + 0.2, c.z), normal = vector.create(0, 1, 0) })
```

## globals/Physics/debugLines {#globals-physics-debuglines}

```lua
Physics.debugLines(opts: table?) -> (PhysicsDebugLines?, string?)
```

Draw what the physics world simulates beyond collider surfaces as
world-space lines, with what each object's lines picture as data. This is
the read the physics debug views draw from: `capture { pass = "physics" }`
and the edit-mode collider overlay (debugViz category `colliders`) both
show these lines.
Layers:
`cage` draws a soft body's elements coloured by load: slack blue, yellow
half way, red at the load it tears (see `PhysicsSoftBodyDiagnostics` for
how a load is read);
`particles` draws its particles: free ones as small grey crosses, pinned
ones as green octahedra, ones held to a rigid body as magenta octahedra;
`contacts` draws the contacts soft surfaces hold against soft surfaces
(their own included): the segment joining the two witness points in
orange, the normal in cyan;
`wheels` draws each `WheelCollider` wheel: its suspension ray as far as
it reaches, its whole travel and the tyre's radius below the mount (green
on ground, red in the air, as the wheel's `isGrounded` reads), the travel
from its mount to the tyre's centre coloured by how much of it is taken
up on the same ramp, its tyre circle in white across its steered axle,
and its ground point (yellow) and normal (cyan).
Answers in edit mode as well as play mode; in edit mode a vehicle stands
at rest with its wheels where they are authored.

**Parameters**

- `opts` `table` _(optional)_ — `{ layers: {string}?, entities: {(string | entityRef)}? }`:
`layers` names the layers to draw (every layer when omitted; an unknown
name is an error naming the layers), `entities` keeps to those entities'
objects (a wheel is kept by its own entity or its chassis').

**Returns** `(PhysicsDebugLines?, string?)` — A `PhysicsDebugLines`, or `nil` and the reason on a VM that holds no physics world (a worker).

```lua
local d = Physics.debugLines(); print(d.lineCount, #d.objects)
for _, o in Physics.debugLines({ layers = { "particles" } }).objects do print(o.entity, o.pinned, o.attached) end
local w = Physics.debugLines({ layers = { "wheels" }, entities = { truck } }); for _, o in w.objects do print(o.index, o.grounded, o.compression) end
```

## globals/Physics/detachSoftRegion {#globals-physics-detachsoftregion}

```lua
Physics.detachSoftRegion(softId: string | entityRef, targetId: (string | entityRef)?) -> number
```

Release regions of a soft body held with `attachSoftRegion` (or
authored in its `SoftBody` component's `attachments`).

**Parameters**

- `softId` `string | entityRef` — The soft body's entity.
- `targetId` `(string | entityRef)` _(optional)_ — Release only the regions held to this rigid body; omit to release every one.

**Returns** `number` — How many attachments were released.

```lua
Physics.detachSoftRegion(flag, pole)
Physics.detachSoftRegion(shell)
```

## globals/Physics/drawnPose {#globals-physics-drawnpose}

```lua
Physics.drawnPose(entityId: string | entityRef) -> PhysicsDrawnPose?
```

Where an entity is drawn this frame, and where its transform puts
it. The simulation advances in fixed ticks (`physics.tick_rate`, 30 a
second by default) while frames are drawn at the display's rate, so a
body with `Physics.interpolation` on is drawn blended between its last two
ticks, and everything parented under it is drawn moved with it.
`entity.position` reports the simulated pose, which moves only when a
tick runs; this read reports the drawn one as well. Read it from a
component's `update` to place anything that has to stay with a moving
body on screen: a camera following it, a label over it, a measurement of
how smoothly it moves.

**Parameters**

- `entityId` `string | entityRef` — Entity id or proxy. Any entity answers; one the simulation
does not blend is drawn at its simulated pose.

**Returns** `PhysicsDrawnPose?` — A `PhysicsDrawnPose`, or `nil` when nothing in the scene answers to that id.

```lua
local d = Physics.drawnPose(car); print(d.position.z, d.simulatedPosition.z, d.interpolated)
local d = Physics.drawnPose(tyre); print(d.interpolated, d.interpolatedBy)
```

## globals/Physics/getAngularVelocity {#globals-physics-getangularvelocity}

```lua
Physics.getAngularVelocity(entityId: (string | entityRef)?) -> vec3?
```

Read the angular velocity of an entity's rigid body.

**Parameters**

- `entityId` `(string | entityRef)` _(optional)_ — Target entity id or proxy; resolves from script context when omitted.

**Returns** `vec3?` — Angular velocity in rad/s, or `nil` if the entity has no rigid body.

```lua
local w = Physics.getAngularVelocity(id)
```

## globals/Physics/getGravity {#globals-physics-getgravity}

```lua
Physics.getGravity() -> vec3
```

Read the current world gravity vector.

**Returns** `vec3` — Gravity vector in m/s² (negative y is "down" in the default world).

```lua
local g = Physics.getGravity()
```

## globals/Physics/getVelocity {#globals-physics-getvelocity}

```lua
Physics.getVelocity(entityId: (string | entityRef)?) -> vec3?
```

Read the linear velocity of an entity's rigid body, or of its soft
body as a whole (its particles' velocities averaged by mass).

**Parameters**

- `entityId` `(string | entityRef)` _(optional)_ — Target entity id or proxy; resolves from script context when omitted.

**Returns** `vec3?` — Velocity in m/s, or `nil` if the entity has no rigid body.

```lua
local v = Physics.getVelocity(id)
```

## globals/Physics/getWheelState {#globals-physics-getwheelstate}

```lua
Physics.getWheelState(entityId: string | entityRef) -> table?
```

Read a wheel collider's runtime state. Reads the native component
the wheel system writes after each physics step.

**Parameters**

- `entityId` `string | entityRef` — Target entity id (must carry a WheelCollider component).

**Returns** `table?` — `{ isGrounded, compression, angularVelocity, rotation, skid, sideImpulse, forwardImpulse, suspensionForce, stopImpulse, center, hitPoint, hitNormal }`, or `nil` if the component is absent. `compression` runs from 0 at the spring's rest up to `suspensionDistance`, where the bump stop is; `rotation` is the wheel's angle about its axle in radians, growing as it rolls forward; `skid` is how fast the tyre slides sideways (m/s) while its grip is used up, 0 while it holds; the impulses are what the tyre applied to the chassis this tick (N·s), and `stopImpulse` what the bump stop applied once the suspension was used up; `center` is the wheel centre in world space.

```lua
local state = Physics.getWheelState(id)
```

## globals/Physics/hasLineOfSight {#globals-physics-haslineofsight}

```lua
Physics.hasLineOfSight(fromId: string, toId: string) -> boolean
```

Check whether two entities have line-of-sight between their
origins.

**Parameters**

- `fromId` `string` — Viewer entity id.
- `toId` `string` — Target entity id.

**Returns** `boolean` — `true` when no collider sits between them (including coincident origins), `false` otherwise.

```lua
if Physics.hasLineOfSight(a, b) then ... end
```

## globals/Physics/ignoreCollision {#globals-physics-ignorecollision}

```lua
Physics.ignoreCollision(entityIdA: string | entityRef, entityIdB: string | entityRef, ignore: boolean?)
```

Keep two entities from colliding, or let them collide again. Covers
every collider each one owns (its rigid body with the shapes composed
onto it, its standalone collider, its ragdoll's bones, its soft body's
surface), so it keeps a soft shell off the chassis it is held to as well
as two rigid bodies apart. An ignored pair holds no contact: the solver
pushes neither one and `Physics.contacts` lists neither to the other.
It applies from the next physics step, to bodies already touching as
well, and lasts until it is turned off, either entity is despawned, or
the mode changes: entering or leaving play starts without it, so make
the call where the bodies are set up for play (a component's `onLoad`).
It stands beside collision groups: a pair either one excludes does not
collide.

**Parameters**

- `entityIdA` `string | entityRef` — First entity id.
- `entityIdB` `string | entityRef` — Second entity id.
- `ignore` `boolean` _(optional)_ — When `true` (default) the pair is kept apart; `false` lets it collide again.

```lua
Physics.ignoreCollision(a, b, true)
```

## globals/Physics/isSleeping {#globals-physics-issleeping}

```lua
Physics.isSleeping(entityId: (string | entityRef)?) -> boolean?
```

Whether an entity's rigid or soft body is currently asleep (at rest
and not simulating). A body sleeps once it stops moving, to save
simulation cost.

**Parameters**

- `entityId` `(string | entityRef)` _(optional)_ — Target entity id or proxy; resolves from script context when omitted.

**Returns** `boolean?` — `true` if asleep, `false` if awake, or `nil` if the entity has neither a rigid nor a soft body.

```lua
if Physics.isSleeping(id) then Physics.wakeUp(id) end
```

## globals/Physics/jointBreaks {#globals-physics-jointbreaks}

```lua
Physics.jointBreaks() -> table
```

Every joint that has broken since the last call to this function.
A joint breaks when the reaction it carries exceeds the `breakForce`
(newtons of linear reaction) or `breakTorque` (the angular row of the same
reaction) its joint was given; each joint
reports once and its constraint is already released when the record
arrives. The 256 most recent are kept: a structure that comes apart while
nothing reads them drops the oldest beyond that, as the engine's own queue
does beyond 1024.

**Returns** `table` — Array of `{ entityId, connectedEntityId, kind, impulse, angularImpulse, force, torque, position }`, oldest first.

```lua
for _, e in ipairs(Physics.jointBreaks()) do print(e.entityId, e.force) end
```

## globals/Physics/jointReaction {#globals-physics-jointreaction}

```lua
Physics.jointReaction(entityId: string | entityRef) -> table?
```

The load an entity's joint is carrying right now, as the constraint
solver resolved it on the last physics step. This is the same quantity a
break threshold is measured against, so it is what to size `breakForce`
and `breakTorque` from. A joint with a soft body at either end reads the
same way.

**Parameters**

- `entityId` `string | entityRef` — Entity carrying the Joint component.

**Returns** `table?` — `{ impulse, angularImpulse, force, torque, position }`, or `nil` when the entity owns no joint.

```lua
local r = Physics.jointReaction(id); print(r and r.force)
```

## globals/Physics/observe {#globals-physics-observe}

```lua
Physics.observe(entityId: (string | entityRef)?, opts: table?) -> PhysicsObservation?
```

Read the solver's own state — the world's accounting, and what it
holds for each body plus why it is not moving one. Every value comes off
the simulation rather than the `Physics` component, so a write the solver
refused or clamped reads back as what it kept. Answers in edit mode as
well as play mode.

**Parameters**

- `entityId` `(string | entityRef)` _(optional)_ — Report on this one entity. Omit for every body in the world.
- `opts` `table` _(optional)_ — `{ bodies: boolean?, contactPoints: boolean? }` — `bodies = false`
builds the world accounting alone, and `contactPoints = false` keeps each
contact pair's normal, depth, impulse and point count while leaving out
the individual points. Both default to true.

**Returns** `PhysicsObservation?` — A `PhysicsObservation`, or `nil` when `entityId` names nothing in the scene. `bodies` is an array, not a table keyed by entity id — each entry names its own entity in `entity`.

```lua
local o = Physics.observe(); for _, b in o.bodies do print(b.entity, b.stillness) end
local o = Physics.observe(id); print(o.bodies[1].stillness, o.bodies[1].stillnessDetail)
```

## globals/Physics/onJointBreak {#globals-physics-onjointbreak}

```lua
Physics.onJointBreak(fn: (table) -> ()) -> () -> ()
```

Call `fn` for every joint that breaks from now on, with the same record
`jointBreaks` returns.

**Parameters**

- `fn` `(table) -> ()` — Receives one break record per broken joint.

**Returns** `() -> ()` — A function that removes this listener.

```lua
local off = Physics.onJointBreak(function(e) print(e.kind, e.force, e.position) end)
```

## globals/Physics/onSoftBodyTear {#globals-physics-onsoftbodytear}

```lua
Physics.onSoftBodyTear(fn: (PhysicsSoftBodyTear) -> ()) -> () -> ()
```

Call `fn` for every tear of a soft body from now on: a body loaded past
its `tearStrain` or `tearForce` (fields of its `SoftBody` component), cut
by `Physics.cutSoftBody` or torn by `Physics.tearSoftBody`. The torn entity
keeps the largest piece; every piece split off is an entity of its own by
the time `fn` runs, standing at the root of the scene and carrying a
`SoftBody` with the torn body's fields, its share of the model and the
material, the velocity, dents and pins that fell on its side. Its body
joins it on the next frame, and an impulse applied to it in `fn` reaches
the body then. A piece is play state: it is left out of every save, and
leaving play takes it away and gives the torn entity back its whole body.

**Parameters**

- `fn` `(PhysicsSoftBodyTear) -> ()` — Receives one `PhysicsSoftBodyTear` per tear: `{ entity, pieces, position, kind, particles }`.

**Returns** `() -> ()` — A function that removes this listener.

```lua
local off = Physics.onSoftBodyTear(function(e) print(e.entity, #e.pieces, e.position) end)
Physics.onSoftBodyTear(function(e) for _, id in e.pieces do Physics.applyImpulse(id, { x = 0, y = 2, z = 0 }) end end)
```

## globals/Physics/overlapSphere {#globals-physics-overlapsphere}

```lua
Physics.overlapSphere(center: vec3, radius: number) -> table
```

Find every entity id whose colliders overlap a sphere.
A trigger (`isTrigger`) holds nothing up, so it is left out.

**Parameters**

- `center` `vec3` — Sphere center in world space.
- `radius` `number` — Sphere radius.

**Returns** `table` — Array of overlapping entity ids.

```lua
local ids = Physics.overlapSphere({x=0,y=0,z=0}, 5)
```

## globals/Physics/pinSoftRegion {#globals-physics-pinsoftregion}

```lua
Physics.pinSoftRegion(softId: string | entityRef, region: PhysicsSoftRegion?, opts: { name: string?, target: vec3? }?) -> string
```

Pin a region of a soft body: its particles are held kinematic, where
they stand now, and the rest of the body hangs from them, rests on them
or swings about them through its elastic elements. A cloth pinned by two
corners hangs; a jelly pinned along its base stays put while its top
wobbles. Give the pin a `target` (or set one later with
`Physics.setPinTarget`) and the pinned particles move together so their
centre stands there, dragging the body after them: a hand grabbing a
corner, a press driven down into a block. The pin is an entry of the soft
body's `SoftBody` component (`pins`), so it is saved with the scene,
reaches every peer and is made again on a rebuilt body; it reads back
from `Physics.softBodyState(softId).pins`.

**Parameters**

- `softId` `string | entityRef` — The soft body's entity (it carries a `SoftBody` component).
- `region` `PhysicsSoftRegion` _(optional)_ — Which particles to pin, as `Physics.softRegion` takes it: `{ box = { min, max } }` or `{ nearest = point }` in the entity's unit box (-0.5 to 0.5 on each axis), `space = "world"` for world coordinates; nil pins the whole body.
- `opts` `{ name: string?, target: vec3? }` _(optional)_ — `{ name: string?, target: vec3? }`: the name the pin is addressed by (by default `pin1`, `pin2`, ...), and a world position to move the pinned particles' centre to.

**Returns** `string` — The pin's name.

```lua
local corner = Physics.pinSoftRegion(cloth, { nearest = {-0.5, 0, 0.5} })
Physics.pinSoftRegion(cloth, { box = { min = {-0.5, -1, 0.45}, max = {0.5, 1, 0.5} } }, { name = "top" })
local grab = Physics.pinSoftRegion(jelly, { nearest = hit.point, space = "world" }, { target = hit.point })
```

## globals/Physics/pumpJointBreaks {#globals-physics-pumpjointbreaks}

```lua
Physics.pumpJointBreaks()
```

Deliver every joint break the simulation has recorded to the registered
listeners. An enabled `Joint` component calls this each tick, so listeners
fire on their own wherever joints come from that component. A joint made by
writing `ecs.PhysicsJoint` directly has no such tick behind it — call this
each frame, or poll `jointBreaks`, to deliver its breaks.

```lua
Physics.pumpJointBreaks()
```

## globals/Physics/pumpSoftBodyTears {#globals-physics-pumpsoftbodytears}

```lua
Physics.pumpSoftBodyTears()
```

Give every piece the soft bodies tore off since the last call its
entity, and deliver each tear to the `onSoftBodyTear` listeners. Every
`SoftBody` component calls this each frame, so tears are handled on their
own; the work is done once per frame however many call it.

```lua
Physics.pumpSoftBodyTears()
```

## globals/Physics/raycast {#globals-physics-raycast}

```lua
Physics.raycast(origin: vec3, direction: vec3, maxDistance: number?, exclude: (string | {string})?) -> table?
```

Cast a ray and return the first hit. Answers from COLLIDERS ALONE: a
mesh that renders but carries no collider is not in the physics world, so a
ray fired through it reports the same `nil` a ray through open air does.
`renderer.raycast` answers the same ray against the geometry the renderer
DRAWS, which is what reads the surface of a terrain, a procedurally
generated mesh, or any plain `Model`.
A trigger (`isTrigger`) holds nothing up, so the ray passes through it.

**Parameters**

- `origin` `vec3` — Ray origin in world space.
- `direction` `vec3` — Ray direction, of any length: the engine normalises it, so
`maxDistance` and `distance` are world units.
- `maxDistance` `number` _(optional)_ — Maximum distance along the ray (defaults to 1000).
- `exclude` `(string | {string})` _(optional)_ — Optional entity id, or array of entity ids, to exclude from hits.

**Returns** `table?` — Hit table `{ entityId, point, normal, distance, startedInside, softBody? }`, or `nil` if nothing hit. `startedInside` is `false` for a surface the query crossed on its way there, and `true` when the query's own start already lay inside that collider — where `distance` is 0, `point` is the start itself, and `normal` is the shortest way out of the collider. `normal` is a unit vector either way. `softBody` is set when what was hit is a soft body, whose surface the query meets as it is deformed now: a `PhysicsSoftHit` naming the surface triangle, the particles carrying it with the hit's weight on each, the particle nearest the hit, and `region`, the hit as a soft region to hand to `Physics.applyImpulse`, `Physics.pinSoftRegion` or `Physics.softParticles`. `Physics.applyImpulseAtPoint` takes the hit's `point` as it is. A `nil` says the ray met no COLLIDER, which `Physics.colliderCount()` separates from a world that holds none for it to meet.

```lua
local hit = Physics.raycast({x=0,y=2,z=0}, {x=0,y=-1,z=0})
local hit = Physics.raycast(origin, dir, 50, { selfId, carriedId })
local hit = Physics.raycast(muzzle, aim, 100); if hit and hit.softBody then Physics.applyImpulseAtPoint(hit.entityId, {x=0, y=0, z=-20}, hit.point, { radius = 0.3 }) end
if Physics.colliderCount() == 0 then hit = renderer.raycast(eye, down, 200) end
```

## globals/Physics/raycastAll {#globals-physics-raycastall}

```lua
Physics.raycastAll(origin: vec3, direction: vec3, maxDistance: number?, maxHits: number?, exclude: (string | {string})?) -> table
```

Cast a ray and return every hit up to `maxHits`. Answers from COLLIDERS
ALONE, so a rendered mesh with no collider is absent from the result;
`renderer.raycastAll` answers the same ray against the geometry the
renderer draws.
A trigger (`isTrigger`) holds nothing up, so the ray passes through it.

**Parameters**

- `origin` `vec3` — Ray origin in world space.
- `direction` `vec3` — Ray direction, of any length: the engine normalises it, so
`maxDistance` and `distance` are world units.
- `maxDistance` `number` _(optional)_ — Optional distance limit along the ray.
- `maxHits` `number` _(optional)_ — Optional cap on the number of hits returned.
- `exclude` `(string | {string})` _(optional)_ — Optional entity id, or array of entity ids, to exclude from hits.

**Returns** `table` — Array of hit tables `{ entityId, point, normal, distance, startedInside, softBody? }` — empty when nothing was hit. `startedInside` is `false` for a surface the query crossed on its way there, and `true` when the query's own start already lay inside that collider — where `distance` is 0, `point` is the start itself, and `normal` is the shortest way out of the collider. `normal` is a unit vector either way. `softBody` is set when what was hit is a soft body, whose surface the query meets as it is deformed now: a `PhysicsSoftHit` naming the surface triangle, the particles carrying it with the hit's weight on each, the particle nearest the hit, and `region`, the hit as a soft region to hand to `Physics.applyImpulse`, `Physics.pinSoftRegion` or `Physics.softParticles`. `Physics.applyImpulseAtPoint` takes the hit's `point` as it is.

```lua
local hits = Physics.raycastAll(origin, dir, 50, 4)
```

## globals/Physics/raycastBetween {#globals-physics-raycastbetween}

```lua
Physics.raycastBetween(fromId: string, toId: string, maxDistance: number?) -> table?
```

Cast a ray from one entity toward another and return the first
hit.

**Parameters**

- `fromId` `string` — Origin entity id.
- `toId` `string` — Target entity id.
- `maxDistance` `number` _(optional)_ — Optional distance cap (default 1000).

**Returns** `table?` — Hit table, or `nil` if the entities are coincident or nothing was hit.

```lua
local hit = Physics.raycastBetween(a, b)
```

## globals/Physics/raycastScreen {#globals-physics-raycastscreen}

```lua
Physics.raycastScreen(sx: number, sy: number, maxDistance: number?, exclude: (string | {string})?) -> table?
```

Cast a ray from a screen pixel into the scene and return the first hit. Unprojects the pixel with `screenToRay`, then casts with `raycast`.

**Parameters**

- `sx` `number` — Screen X in viewport-local pixels (the space of `input.mouse_position` and `screenToRay`).
- `sy` `number` — Screen Y in viewport-local pixels.
- `maxDistance` `number` _(optional)_ — Maximum distance along the ray (defaults to 1000, matching `raycast`).
- `exclude` `(string | {string})` _(optional)_ — Optional entity id, or array of entity ids, to exclude from hits.

**Returns** `table?` — Hit table `{ entityId, point, normal, distance, startedInside, softBody? }`, or `nil` on a miss or when no camera has rendered yet. `startedInside` is `false` for a surface the query crossed on its way there, and `true` when the query's own start already lay inside that collider — where `distance` is 0, `point` is the start itself, and `normal` is the shortest way out of the collider. `normal` is a unit vector either way. `softBody` is set when what was hit is a soft body, whose surface the query meets as it is deformed now: a `PhysicsSoftHit` naming the surface triangle, the particles carrying it with the hit's weight on each, the particle nearest the hit, and `region`, the hit as a soft region to hand to `Physics.applyImpulse`, `Physics.pinSoftRegion` or `Physics.softParticles`. `Physics.applyImpulseAtPoint` takes the hit's `point` as it is.

```lua
local Zin = require("@builtin::modules.zinput")
local x, y = Zin.state.mousePosition(); local hit = Physics.raycastScreen(x, y)
```

## globals/Physics/releasePin {#globals-physics-releasepin}

```lua
Physics.releasePin(softId: string | entityRef, name: string?) -> number
```

Release a pin of a soft body, or every pin: its particles take their
mass back, keep the velocity they had, and move with the body again.

**Parameters**

- `softId` `string | entityRef` — The soft body's entity.
- `name` `string` _(optional)_ — The pin to release; omit to release every one.

**Returns** `number` — How many pins were released.

```lua
Physics.releasePin(cloth, corner)
Physics.releasePin(cloth)
```

## globals/Physics/removeCollider {#globals-physics-removecollider}

```lua
Physics.removeCollider(entityId: string | entityRef) -> string?
```

Remove whichever collider component an entity carries.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.

**Returns** string? The component that was removed, or nil when there was none.

```lua
Physics.removeCollider(id)
```

## globals/Physics/removeConstraint {#globals-physics-removeconstraint}

```lua
Physics.removeConstraint(entityId: string | entityRef, index: number?)
```

Remove transform constraints from an entity (if any are
present).

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `index` `number` _(optional)_ — Optional constraint index (currently ignored — the whole component is removed).

```lua
Physics.removeConstraint(id)
```

## globals/Physics/removeJoint {#globals-physics-removejoint}

```lua
Physics.removeJoint(entityId: string | entityRef)
```

Remove the Joint component from an entity (if present).

**Parameters**

- `entityId` `string | entityRef` — Target entity id.

```lua
Physics.removeJoint(id)
```

## globals/Physics/removeWheelCollider {#globals-physics-removewheelcollider}

```lua
Physics.removeWheelCollider(entityId: string | entityRef)
```

Remove the WheelCollider component from an entity (if present).

**Parameters**

- `entityId` `string | entityRef` — Target entity id.

```lua
Physics.removeWheelCollider(id)
```

## globals/Physics/repairSoftBody {#globals-physics-repairsoftbody}

```lua
Physics.repairSoftBody(softId: string | entityRef) -> number
```

Restore the shape a soft body was built in: every permanent dent,
crumple and crease it took past its `plasticYield` is undone, and the body
is put back in that shape where it stands, keeping its motion as a whole. The repair is the
`SoftBody` component's `repairs` count raised by one, so it reaches every
peer. `Physics.softBodyState(softId).plastic` reads 0 once it is applied
(at the start of the next frame).

**Parameters**

- `softId` `string | entityRef` — The soft body's entity (it carries a `SoftBody` component).

**Returns** `number` — The component's new `repairs` count.

```lua
Physics.repairSoftBody(shell)
```

## globals/Physics/setAngularDamping {#globals-physics-setangulardamping}

```lua
Physics.setAngularDamping(entityIdOrDamping: string | entityRef | number, damping: number?)
```

Set angular damping on an entity's rigid body. One-arg form
targets the script-context entity.

**Parameters**

- `entityIdOrDamping` `string | entityRef | number` — Entity id (with `damping`) OR damping value (script-context entity).
- `damping` `number` _(optional)_ — Optional explicit damping when targeting another entity.

```lua
Physics.setAngularDamping(0.1)
Physics.setAngularDamping(entityId, 0.1)
```

## globals/Physics/setAngularVelocity {#globals-physics-setangularvelocity}

```lua
Physics.setAngularVelocity(a: string | entityRef | number | vec3, b: (number | vec3)?, c: number?, d: number?)
```

Set the angular velocity of an entity (radians/sec). Same call
shapes as `setVelocity`.

**Parameters**

- `a` `string | entityRef | number | vec3` — x-component, a `{x, y, z}` vector, or an entity id (explicit target).
- `b` `(number | vec3)` _(optional)_ — y-component, x-component, or the vector depending on call form.
- `c` `number` _(optional)_ — z-component or y-component depending on call form.
- `d` `number` _(optional)_ — Optional z-component when targeting an explicit entity.

```lua
Physics.setAngularVelocity(0, 0, 1)
Physics.setAngularVelocity(entityId, 0, 0, 1)
Physics.setAngularVelocity(entityId, {x=0, y=0, z=1})
```

## globals/Physics/setBodyType {#globals-physics-setbodytype}

```lua
Physics.setBodyType(entityId: string | entityRef, bodyType: string)
```

Change a rigid body's type at runtime. Mass, colliders, and
joints are preserved — only the body's response to forces and
position writes changes.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `bodyType` `string` — One of `"dynamic"`, `"kinematic"`, `"static"`.

```lua
Physics.setBodyType(entityId, "kinematic")
```

## globals/Physics/setCcdEnabled {#globals-physics-setccdenabled}

```lua
Physics.setCcdEnabled(entityIdOrEnabled: string | entityRef | boolean, enabled: boolean?)
```

Enable or disable continuous collision detection on an entity's
rigid body. One-arg form targets the script-context entity.

**Parameters**

- `entityIdOrEnabled` `string | entityRef | boolean` — Entity id (with `enabled`) OR boolean (script-context entity).
- `enabled` `boolean` _(optional)_ — Optional explicit boolean when targeting another entity.

```lua
Physics.setCcdEnabled(true)
Physics.setCcdEnabled(entityId, true)
```

## globals/Physics/setCollisionGroups {#globals-physics-setcollisiongroups}

```lua
Physics.setCollisionGroups(entityId: string | entityRef, membership: number, filter: number)
```

Set the collision-group membership and filter bitmasks on an
entity's colliders. Adds a `CollisionGroup` component if missing; the
component holds the groups, so they apply to the colliders of the
entity's rigid body or standalone collider by the next physics step, and
a body built or rebuilt later takes them as it is built, in edit mode as
well as play. A soft body's groups are its `SoftBody` component's
`collisionMemberships` and `collisionFilter`.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `membership` `number` — Bitmask: which groups this collider belongs to.
- `filter` `number` — Bitmask: which groups this collider can collide with.

```lua
Physics.setCollisionGroups(id, 0x0001, 0xFFFF)
```

## globals/Physics/setGravity {#globals-physics-setgravity}

```lua
Physics.setGravity(gravity: vec3)
```

Replace the world gravity vector.

**Parameters**

- `gravity` `vec3` — New gravity vector in m/s².

```lua
Physics.setGravity({x=0, y=-9.81, z=0})
```

## globals/Physics/setGravityScale {#globals-physics-setgravityscale}

```lua
Physics.setGravityScale(entityIdOrScale: string | entityRef | number, scale: number?)
```

Set the per-entity gravity scale (1.0 = normal, 0.0 = no
gravity). One-arg form targets the script-context entity.

**Parameters**

- `entityIdOrScale` `string | entityRef | number` — Entity id (with `scale`) OR scale value (script-context entity).
- `scale` `number` _(optional)_ — Optional explicit scale when targeting another entity.

```lua
Physics.setGravityScale(0.5)
Physics.setGravityScale(entityId, 0.5)
```

## globals/Physics/setJointMotor {#globals-physics-setjointmotor}

```lua
Physics.setJointMotor(entityId: string | entityRef, targetVelocity: number, maxForce: number)
```

Set a motor on an entity's joint.

**Parameters**

- `entityId` `string | entityRef` — Target entity id (must carry a Joint component).
- `targetVelocity` `number` — Desired joint velocity.
- `maxForce` `number` — Maximum force the motor can apply.

```lua
Physics.setJointMotor(id, 5.0, 1000)
```

## globals/Physics/setLinearDamping {#globals-physics-setlineardamping}

```lua
Physics.setLinearDamping(entityIdOrDamping: string | entityRef | number, damping: number?)
```

Set linear damping on an entity's rigid body (0 = no damping).
One-arg form targets the script-context entity.

**Parameters**

- `entityIdOrDamping` `string | entityRef | number` — Entity id (with `damping`) OR damping value (script-context entity).
- `damping` `number` _(optional)_ — Optional explicit damping when targeting another entity.

```lua
Physics.setLinearDamping(0.05)
Physics.setLinearDamping(entityId, 0.05)
```

## globals/Physics/setMass {#globals-physics-setmass}

```lua
Physics.setMass(entityIdOrMass: string | entityRef | number, mass: number?)
```

Set the mass of an entity's rigid body (kg). One-arg form
targets the script-context entity.

**Parameters**

- `entityIdOrMass` `string | entityRef | number` — Entity id (with `mass`) OR mass value (script-context entity).
- `mass` `number` _(optional)_ — Optional explicit mass when targeting another entity.

```lua
Physics.setMass(10)
Physics.setMass(entityId, 10)
```

## globals/Physics/setPinTarget {#globals-physics-setpintarget}

```lua
Physics.setPinTarget(softId: string | entityRef, name: string, position: vec3?)
```

Drive a pin of a soft body to a world position: its particles move
together over the next step so the centre they had when the pin was made
stands at `position`, with the velocity that carries them there, and the
rest of the body follows. Set it every frame to drag the region along a
path. `nil` stops driving it and holds it where it stands. The target is
the pin's entry in the `SoftBody` component's `pins`, so it reaches every
peer.

**Parameters**

- `softId` `string | entityRef` — The soft body's entity.
- `name` `string` — The pin's name, as `pinSoftRegion` returned it.
- `position` `vec3` _(optional)_ — The world position for the pinned particles' centre; nil holds them where they stand.

```lua
Physics.setPinTarget(cloth, corner, {x=2, y=3, z=0})
```

## globals/Physics/setRotationLocks {#globals-physics-setrotationlocks}

```lua
Physics.setRotationLocks(entityId: string | entityRef, x: boolean, y: boolean, z: boolean)
```

Lock or unlock rotation on specific axes.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `x` `boolean` — Lock rotation about the world X axis.
- `y` `boolean` — Lock rotation about the world Y axis.
- `z` `boolean` — Lock rotation about the world Z axis.

```lua
Physics.setRotationLocks(id, false, true, false)
```

## globals/Physics/setTranslationLocks {#globals-physics-settranslationlocks}

```lua
Physics.setTranslationLocks(entityId: string | entityRef, x: boolean, y: boolean, z: boolean)
```

Lock or unlock translation on specific axes.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `x` `boolean` — Lock translation along the world X axis.
- `y` `boolean` — Lock translation along the world Y axis.
- `z` `boolean` — Lock translation along the world Z axis.

```lua
Physics.setTranslationLocks(id, false, false, true)
```

## globals/Physics/setVelocity {#globals-physics-setvelocity}

```lua
Physics.setVelocity(a: string | entityRef | number | vec3, b: (number | vec3)?, c: number?, d: number?)
```

Set the linear velocity of an entity. Accepts `(x, y, z)` or a
`{x, y, z}` vector for the script-context entity, or the same
prefixed with an explicit `entityId`.

**Parameters**

- `a` `string | entityRef | number | vec3` — x-component, a `{x, y, z}` vector, or an entity id (explicit target).
- `b` `(number | vec3)` _(optional)_ — y-component, x-component, or the vector depending on call form.
- `c` `number` _(optional)_ — z-component or y-component depending on call form.
- `d` `number` _(optional)_ — Optional z-component when targeting an explicit entity.

```lua
Physics.setVelocity(0, 10, 0)
Physics.setVelocity(entityId, 0, 10, 0)
Physics.setVelocity(entityId, {x=0, y=10, z=0})
```

## globals/Physics/softBodySettings {#globals-physics-softbodysettings}

```lua
Physics.softBodySettings(changes: table?) -> PhysicsSoftBodySettings
```

The settings every soft body of the world is simulated under, and the
way to change them: how many extra substeps a hard hit may run, how stiff
soft contacts are, which tangle and overlap recovery runs, and how far
the FEM solver (`SoftBody.solver = "fem"`) solves each step. Each one
trades simulation quality for cost; `Physics.stepCost().softBodies` shows
what each body costs under them. They are world settings
(`physics.soft_body_*` in `.world_settings`) saved with the world: a
change reads back at once and the solver runs under it from the next
physics tick. Written during play, they land on the play shadow like any
other `/source` write: the solver runs under them at once, and they are
gone when the play edits are discarded. Write them in edit mode (or keep
them with `vfs.promotePlayShadow("/zero/source/.world_settings")`) for the
world to keep them.

**Parameters**

- `changes` `table` _(optional)_ — Optional `PhysicsSoftBodySettings` fields to change; the fields
left out keep their values. A field the settings do not have, or a value
of the wrong type or out of range, is refused by name.

**Returns** `PhysicsSoftBodySettings` — The settings with `changes` applied: what the next tick runs under.

```lua
print(Physics.softBodySettings().maxExtraSubsteps)
Physics.softBodySettings({ maxExtraSubsteps = 2 })   -- cheaper hard hits
Physics.softBodySettings({ tangleDetection = false, overlapConstraints = false })
Physics.softBodySettings({ femMaxIterations = 60 })   -- FEM bodies converge further each substep
```

## globals/Physics/softBodyState {#globals-physics-softbodystate}

```lua
Physics.softBodyState(entityId: string | entityRef, opts: table?) -> PhysicsSoftBodyState?
```

What the solver holds for one soft body (an entity with a `SoftBody`
component): its particle, cell and edge counts, the volume it encloses
against its rest volume, its centre of mass and frame, the box around its
particles, how fast they move, whether it sleeps, and what it touches,
and in `render` how the entity's model is drawn carried by the body.
`plastic` is how deep the deformation that stayed is, in metres (a dent
past the component's `plasticYield`; `plasticMean` the mean over the
particles), 0 for a body that only deformed elastically.
`diverged` is true while a step has carried the body into a state no
material reaches (its particles spread, or its volume grown, past ten
times the shape it is held at, or a particle carried to a value that is
not a number), and `divergence` says what was measured; the engine log
names the body the step it diverges.
`tearStrain` and `tearForce` are the thresholds it tears past (0 tears
never), and `piece` names the torn piece a body split off another
continues (nil for a body built from its fields).
`attachments` lists the regions held to rigid bodies (`attachSoftRegion`)
with the force each pulls with, `attachedParticles` counts the particles
tied, and `clusters` counts the body's frame plus one per joint end
anchored on a region of it. `pins` lists the regions pinned
(`pinSoftRegion`) with how many particles each holds, its target and
where its particles' centre stands, and `pinnedParticles` counts the
particles held kinematic. `diagnostics` is what the physics debug views
picture of the body as numbers: inverted cells, self-crossings, element
loads (`maxLoad`, `loadedElements`), pinned and attached particles (see
`PhysicsSoftBodyDiagnostics`; `Physics.debugLines` draws them).
`replication`, on a synced entity in a multiplayer session, says which
peer simulates the body (`authority` `"local"` or `"remote"`, `owner`)
and what it costs on the wire here (`lastSync` in seconds,
`bytesPerSecond`).
`status` says where the body stands: `"built"`, `"pending"` (the
component stands and the body is not built yet, `waitingFor` naming what
the build waits on), `"refused"` (`refusal` says why the component was
not built) or `"none"` (no SoftBody component). A body is built at the
start of the frame after its component is added, so a read in the call
that added it answers `"pending"`; wait on `status` rather than reading
`exists = false` as a refusal. Answers in edit mode as well as play mode.

**Parameters**

- `entityId` `string | entityRef` — Entity id or proxy.
- `opts` `table` _(optional)_ — `{ positions: boolean?, contactPoints: boolean?, contactsWith: (string | entityRef)? }`:
`positions = true` adds every particle's world position;
`contactPoints = true` adds each contact's individual points (each
contact names its count, normal, depth, impulse and particles either
way); `contactsWith` reports only the contacts with that one entity, so a
read about one pair costs that pair alone.

**Returns** `PhysicsSoftBodyState?` — A `PhysicsSoftBodyState`, or `nil` when nothing in the scene answers to that id.

```lua
local s = Physics.softBodyState(jelly); print(s.particleCount, s.volumeRatio, s.sleeping)
print(Physics.softBodyState(shell).plastic)   -- metres of dent the shell kept
local s = Physics.softBodyState(shell); if s.diverged then print(s.divergence) end
for _, p in Physics.softBodyState(jelly, { positions = true }).positions do print(p.y) end
local r = Physics.softBodyState(jelly).render; print(r.drawn, r.vertexCount, r.uploadedThisFrame)
local s = Physics.softBodyState(jelly); if s.status == "pending" then print("waiting for", s.waitingFor) end
```

## globals/Physics/softBodyTears {#globals-physics-softbodytears}

```lua
Physics.softBodyTears() -> { PhysicsSoftBodyTear }
```

Every soft body tear since the last call to this function, oldest
first, with the entities of the pieces each split off: the same records
`onSoftBodyTear` delivers. The 256 most recent are kept.

**Returns** `{ PhysicsSoftBodyTear }` — Array of `PhysicsSoftBodyTear`.

```lua
for _, t in Physics.softBodyTears() do print(t.kind, t.entity, #t.pieces) end
```

## globals/Physics/softParticles {#globals-physics-softparticles}

```lua
Physics.softParticles(softId: string | entityRef, region: PhysicsSoftRegion?) -> PhysicsSoftParticles?
```

Read the particles of a soft body that a region selects: where each
stands, how fast it moves and whether it is pinned, with the selection's
centre of mass and mean velocity. The way to see a dent where a hit
landed, how far a pinned corner was dragged, or how a region moves.
`indices` name the particles for comparing two reads of the same body.
The region is resolved at each call against where the particles stand
then (a local-space region in the body's frame as it stands now), so a
read after the body moved can select other particles than an earlier
read of the same region; a pin's own particles read back through
`Physics.softBodyState(softId).pins` (`center`) and the `pinned` flags.

**Parameters**

- `softId` `string | entityRef` — The soft body's entity.
- `region` `PhysicsSoftRegion` _(optional)_ — Which particles to read, as `Physics.softRegion` takes it; nil reads the whole body.

**Returns** `PhysicsSoftParticles?` — A `PhysicsSoftParticles` report, or nil when nothing in the scene answers to that id.

```lua
local top = Physics.softParticles(jelly, { box = { min = {-0.5, 0.4, -0.5}, max = {0.5, 0.5, 0.5} } }); print(top.count, top.center.y)
local near = Physics.softParticles(shell, { box = { min = {1.6, 0, -0.3}, max = {2.2, 1, 0.3} }, space = "world" })
```

## globals/Physics/softRegion {#globals-physics-softregion}

```lua
Physics.softRegion(region: PhysicsSoftRegion?) -> { [string]: any }
```

The engine form of a soft body region: the record the `SoftBody`
component's attachments and a `Joint`'s soft body ends carry. A region
names part of a soft body by where its particles stand: `{ box = { min,
max } }` is every particle between the two corners, `{ nearest = point }`
the one particle nearest the point, and `nil` (or `{}`) the whole body.
Coordinates are in the soft body entity's local space, the unit box it
is built in (-0.5 to 0.5 on each axis, before its scale), so a region
stays the same part of the body wherever the body is; `space = "world"`
reads them in world space instead. A box counts a particle whose contact
skin reaches into it: a generated body's outer particles stand that skin
inside the unit box, so a box flat on one of its faces
(`min = {-0.5, -1, -1}, max = {-0.5, 1, 1}`) holds that face's particles.
Errors naming the key that is wrong.

**Parameters**

- `region` `PhysicsSoftRegion` _(optional)_ — `{ box = { min = v, max = v } | { v, v }, nearest = v, space = "local" | "world" }`, or nil for the whole body.

**Returns** `{ [string]: any }` — `{ kind = "Whole" | "Box" | "Nearest", min, max, point, world }`.

```lua
Physics.softRegion({ box = { min = {-0.5, 0.45, -0.5}, max = {0.5, 0.5, 0.5} } })
Physics.softRegion({ nearest = {0, 0.5, 0} })
```

## globals/Physics/sphereCast {#globals-physics-spherecast}

```lua
Physics.sphereCast(origin: vec3, radius: number, direction: vec3, maxDistance: number?, exclude: (string | {string})?) -> table?
```

Cast a sphere along a direction and return the first hit.
A trigger (`isTrigger`) holds nothing up, so the sweep passes through it.

**Parameters**

- `origin` `vec3` — Sphere center at the start of the cast.
- `radius` `number` — Sphere radius.
- `direction` `vec3` — Cast direction, of any length: the engine normalises it, so
`maxDistance` and `distance` are world units.
- `maxDistance` `number` _(optional)_ — Optional distance limit.
- `exclude` `(string | {string})` _(optional)_ — Optional entity id, or array of entity ids, to exclude from hits.
Applied while sweeping rather than to the answer, so a cast that starts
inside an excluded collider reports what is behind it.

**Returns** `table?` — Hit table `{ entityId, point, normal, distance, startedInside, softBody? }`, or `nil` if nothing hit. `startedInside` is `false` for a surface the query crossed on its way there, and `true` when the query's own start already lay inside that collider — where `distance` is 0, `point` is the start itself, and `normal` is the shortest way out of the collider. `normal` is a unit vector either way. `softBody` is set when what was hit is a soft body, whose surface the query meets as it is deformed now: a `PhysicsSoftHit` naming the surface triangle, the particles carrying it with the hit's weight on each, the particle nearest the hit, and `region`, the hit as a soft region to hand to `Physics.applyImpulse`, `Physics.pinSoftRegion` or `Physics.softParticles`. `Physics.applyImpulseAtPoint` takes the hit's `point` as it is.

```lua
local hit = Physics.sphereCast(o, 0.5, dir, 10)
local hit = Physics.sphereCast(o, 0.5, dir, 10, selfId)
```

## globals/Physics/stepCost {#globals-physics-stepcost}

```lua
Physics.stepCost() -> PhysicsStepCost?
```

What the last physics step cost, stage by stage — the same figures
`worldState().step` carries, for a caller that wants only these. Each
covers that one step rather than a window of them, and consecutive steps
over the same resting scene vary by tens of percent, so several samples
averaged is the honest read of what a step costs. `softBodyMs` is the
soft body part of the step and `softBodies` each soft body's part of it.
A frame can run several steps; `recent` lists the last 64 with their
ordinals (`step`), so a loop reading once a frame counts every step.

**Returns** `PhysicsStepCost?` — A `PhysicsStepCost`, or `nil` on a frame where the pipeline did not step — a paused simulation, or a world still bootstrapping.

```lua
local c = Physics.stepCost(); if c then print(c.stepMs, c.narrowPhaseMs) end
local c = Physics.stepCost(); if c then for _, b in c.softBodies do print(b.entity, b.ms, b.particles) end end
local seen, total = 0, 0
-- once a frame: every step since the last read, however many a frame ran
local c = Physics.stepCost(); if c then for _, s in c.recent do if s.step > seen then total += s.softBodyMs; seen = s.step end end end
```

## globals/Physics/stillnessReasons {#globals-physics-stillnessreasons}

```lua
Physics.stillnessReasons() -> { string }
```

Every reason `whyStill` can answer with, in the order the engine
considers them. Read from the engine, so the list is the one the answers
come from.

**Returns** `{ string }` — An array of reason names.

```lua
for _, reason in Physics.stillnessReasons() do print(reason) end
```

## globals/Physics/tearSoftBody {#globals-physics-tearsoftbody}

```lua
Physics.tearSoftBody(softId: string | entityRef, region: PhysicsSoftRegion)
```

Tear a soft body at a region: every edge and cell holding a particle of
the region comes apart there, opening a hole or, when the region runs
across the body, splitting it. Applied straight after the next physics
step; a piece the tear separates becomes an entity of its own (see
`onSoftBodyTear`), and it reports as a tear of kind "tear". Raises when the
entity carries no `SoftBody`, for a region naming no box and no point (the
whole body), and in edit mode, where nothing simulates.

**Parameters**

- `softId` `string | entityRef` — The soft body's entity.
- `region` `PhysicsSoftRegion` — Which particles to tear through, as `Physics.softRegion` takes it: `{ box = { min, max } }` or `{ nearest = point }` in the entity's unit box (-0.5 to 0.5 on each axis), `space = "world"` for world coordinates.

```lua
Physics.tearSoftBody(cloth, { nearest = hit.point, space = "world" })
Physics.tearSoftBody(sheet, { box = { min = {-0.02, -1, -0.5}, max = {0.02, 1, 0.5} } })
```

## globals/Physics/touching {#globals-physics-touching}

```lua
Physics.touching(entityId: string | entityRef, otherId: string | entityRef) -> (boolean, number, { PhysicsContactPoint })
```

Whether two entities are touching, and how deeply.

**Parameters**

- `entityId` `string | entityRef` — Entity id or proxy.
- `otherId` `string | entityRef` — The other entity id or proxy.

**Returns** `(boolean, number, { PhysicsContactPoint })` — `(touching, deepestPenetration, points)` — `deepestPenetration` is in metres and `0` for surfaces that meet without overlapping.

```lua
local hit, depth = Physics.touching(a, b); print(hit, depth)
```

## globals/Physics/vehicleState {#globals-physics-vehiclestate}

```lua
Physics.vehicleState(chassisId: string | entityRef) -> table?
```

Read a vehicle: the chassis body's forward speed and the state of
every wheel under it (the wheels whose nearest rigid-body ancestor is
this chassis).

**Parameters**

- `chassisId` `string | entityRef` — The chassis entity (the rigid body the wheels sit under).

**Returns** `table?` — `{ speed, groundedWheels, wheels }`, or `nil` when the chassis has no WheelCollider under it. `speed` is metres per second along the chassis' forward (-Z), negative in reverse; `wheels` lists `getWheelState` tables, each with its `entityId`, in hierarchy order.

```lua
local car = Physics.vehicleState(carId); print(car.speed, car.groundedWheels)
```

## globals/Physics/wakeUp {#globals-physics-wakeup}

```lua
Physics.wakeUp(entityId: (string | entityRef)?)
```

Wake an entity's sleeping rigid or soft body so it resumes
simulating. The motion setters (`applyImpulse`, `setVelocity`,
`setAngularVelocity`) wake the body for you; call this to wake one
explicitly.

**Parameters**

- `entityId` `(string | entityRef)` _(optional)_ — Target entity id or proxy; resolves from script context when omitted.

```lua
Physics.wakeUp(id)
```

## globals/Physics/whyStill {#globals-physics-whystill}

```lua
Physics.whyStill(entityId: string | entityRef) -> (string?, string?)
```

Why the solver is not moving a body. Returns `nil` when it IS moving
it, and otherwise one of `noBody`, `simulationNotStepping`, `disabled`,
`static`, `kinematic`, `infiniteMass`, `translationLocked`,
`gravityDisabled`, `asleep`, `outsideIsland`, `resting`, `aboutToMove` —
the nearest cause, so the answer names the thing to change. A second return
carries the detail: which collider it rests on and how deeply, what its
effective gravity works out to, and so on.

**Parameters**

- `entityId` `string | entityRef` — Entity id or proxy.

**Returns** `(string?, string?)` — `(reason, detail)`.

```lua
local why, detail = Physics.whyStill(id); if why then print(why, detail) end
```

## globals/Physics/worldState {#globals-physics-worldstate}

```lua
Physics.worldState() -> PhysicsWorldState
```

How many bodies, colliders, joints and contacts the simulation holds
right now, with world gravity, the timestep, whether the pipeline is
stepping at all, and what the last step cost. Counted off the solver, so
a body that failed to build is absent here while its `Physics` component
still exists.

**Returns** `PhysicsWorldState` — A `PhysicsWorldState`.

```lua
local w = Physics.worldState(); print(w.bodies.awake .. "/" .. w.bodies.total .. " awake")
print(Physics.worldState().contacts.touchingPairs .. " pairs touching")
```
