---
title: "physics"
description: "The physics namespace — the engine's Luau API reference for physics."
section: "API Reference"
slug: "api-physics"
canonical: "https://origozero.ai/docs/api-physics"
updated: "2026-09-05T23:13:46.969216179+00:00"
tags: ["api", "reference"]
---

# physics

The `physics` namespace — 60 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).

```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 })
```

## 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.

**Parameters**

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

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

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

```lua
physics.applyForce(entityIdOrForce: string | entityRef | vec3, force: vec3?)
```

Apply a force to an entity's rigid 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.

**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.

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

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

```lua
physics.applyForceAtPoint(entityId: string | entityRef, force: vec3, point: vec3)
```

Apply a force at a specific world-space point — generates the
matching torque from the lever arm.

**Parameters**

- `entityId` `string | entityRef` — Target entity id.
- `force` `vec3` — Force vector.
- `point` `vec3` — World-space application point.

```lua
Physics.applyForceAtPoint(id, {x=0,y=10,z=0}, {x=1,y=0,z=0})
```

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

```lua
physics.applyImpulse(entityIdOrImpulse: string | entityRef | vec3, impulse: vec3?)
```

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.

**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.

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

## 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/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 — or `nil` when nothing in the scene answers to that id.

```lua
local b = Physics.bodyState(id); print(b.bodyType, b.mass, b.stillness)
if not Physics.bodyState(id).exists then print("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.

**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.
- `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 }`, 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.

```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.

**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.
- `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 }`, 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.

```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) -> { 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.

**Parameters**

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

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

```lua
for _, c in Physics.contacts(id) do print(c.other, c.deepestPenetration) end
```

## 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.

**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 }`, or `nil` if the component is absent.

```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?)
```

Toggle ignored-collision state between two specific entities.

**Parameters**

- `entityIdA` `string | entityRef` — First entity id.
- `entityIdB` `string | entityRef` — Second entity id.
- `ignore` `boolean` _(optional)_ — When `true` (default) collisions between the pair are skipped.

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

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

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

Whether an entity's rigid 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 no rigid 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.

**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/overlapSphere {#globals-physics-overlapsphere}

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

Find every entity id whose colliders overlap a sphere.

**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/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/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`.

**Parameters**

- `origin` `vec3` — Ray origin in world space.
- `direction` `vec3` — Ray direction (does not need to be unit-length; the engine normalises).
- `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 }`, 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. 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 })
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.

**Parameters**

- `origin` `vec3` — Ray origin in world space.
- `direction` `vec3` — Ray direction.
- `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 }` — 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.

```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 }`, 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.

```lua
local m = input.mouse_position; local hit = Physics.raycastScreen(m[1], m[2])
```

## 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/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.

**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/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/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.

**Parameters**

- `origin` `vec3` — Sphere center at the start of the cast.
- `radius` `number` — Sphere radius.
- `direction` `vec3` — Cast direction.
- `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 }`, 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.

```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.

**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
```

## 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/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/wakeUp {#globals-physics-wakeup}

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

Wake an entity's sleeping rigid 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")
```
