raycast(origin: vec3, direction: vec3, maxDistance: number?, exclude: (string | {string}) →
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`.
`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.
| arg | type | description |
|---|
| origin | vec3 | Ray origin in world space. |
| direction | vec3 | Ray direction (does not need to be unit-length; the engine normalises). |
| maxDistance | number? | Maximum distance along the ray (defaults to 1000). |
| exclude | (string | {string} | Optional entity id, or array of entity ids, to exclude from hits. |
examples
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
colliderCount( ) →
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.
examples
if Physics.colliderCount() == 0 then print("nothing here is solid") endraycastAll(origin: vec3, direction: vec3, maxDistance: number?, maxHits: number?, exclude: (string | {string}) →
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.
`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.
| arg | type | description |
|---|
| origin | vec3 | Ray origin in world space. |
| direction | vec3 | Ray direction. |
| maxDistance | number? | Optional distance limit along the ray. |
| maxHits | number? | Optional cap on the number of hits returned. |
| exclude | (string | {string} | Optional entity id, or array of entity ids, to exclude from hits. |
examples
local hits = Physics.raycastAll(origin, dir, 50, 4)
raycastScreen(sx: number, sy: number, maxDistance: number?, exclude: (string | {string}) →
Cast a ray from a screen pixel into the scene and return the first hit. Unprojects the pixel with `screenToRay`, then casts with `raycast`.
`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.
| arg | type | description |
|---|
| 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? | Maximum distance along the ray (defaults to 1000, matching `raycast`). |
| exclude | (string | {string} | Optional entity id, or array of entity ids, to exclude from hits. |
examples
local m = input.mouse_position; local hit = Physics.raycastScreen(m[1], m[2])
overlapSphere(center: vec3, radius: number) →
Find every entity id whose colliders overlap a sphere.
| arg | type | description |
|---|
| center | vec3 | Sphere center in world space. |
| radius | number | Sphere radius. |
examples
local ids = Physics.overlapSphere({x=0,y=0,z=0}, 5)sphereCast(origin: vec3, radius: number, direction: vec3, maxDistance: number?, exclude: (string | {string}) →
Cast a sphere along a direction and return the first hit.
Applied while sweeping rather than to the answer, so a cast that starts
inside an excluded collider reports what is behind it.
`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.
| arg | type | description |
|---|
| origin | vec3 | Sphere center at the start of the cast. |
| radius | number | Sphere radius. |
| direction | vec3 | Cast direction. |
| maxDistance | number? | Optional distance limit. |
| exclude | (string | {string} | Optional entity id, or array of entity ids, to exclude from hits. |
examples
local hit = Physics.sphereCast(o, 0.5, dir, 10)
local hit = Physics.sphereCast(o, 0.5, dir, 10, selfId)
capsuleCast(origin: vec3, radius: number, halfHeight: number, direction: vec3, maxDistance: number?, exclude: (string | {string}) →
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.
stands `halfHeight + radius` tall in each direction.
Applied while sweeping rather than to the answer, so a cast that starts
inside an excluded collider reports what is behind it.
`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.
| arg | type | description |
|---|
| 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 |
| direction | vec3 | Cast direction. |
| maxDistance | number? | Optional distance limit. |
| exclude | (string | {string} | Optional entity id, or array of entity ids, to exclude from hits. |
examples
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)
boxCast(origin: vec3, halfExtents: vec3, direction: vec3, maxDistance: number?, exclude: (string | {string}) →
Cast a box along a direction and return the first hit.
Applied while sweeping rather than to the answer, so a cast that starts
inside an excluded collider reports what is behind it.
`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.
| arg | type | description |
|---|
| 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 distance limit. |
| exclude | (string | {string} | Optional entity id, or array of entity ids, to exclude from hits. |
examples
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 })colliderShapes(entityId: string | entityRef) →
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.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
examples
local shapes = Physics.colliderShapes(id)
colliderManifest( ) →
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.
examples
for _, c in ipairs(Physics.colliderManifest()) do print(c.entity, c.role) end
colliderGeometry(options: 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.
entry of `colliders` is `{ entity, colliderName?, shapeType, role, exact, firstIndex, indexCount }`.
| arg | type | description |
|---|
| options | table? | `{ tessellation = "low"|"medium"|"high", colors = { {r,g,b,a}, ... }, defaultColor = {r,g,b,a} }`. |
examples
local geo = Physics.colliderGeometry({ tessellation = "high" })getGravity( ) →
Read the current world gravity vector.
examples
local g = Physics.getGravity()
setGravity(gravity: vec3) → void
Replace the world gravity vector.
| arg | type | description |
|---|
| gravity | vec3 | New gravity vector in m/s². |
examples
Physics.setGravity({x=0, y=-9.81, z=0})getVelocity(entityId: (string | entityRef) →
Read the linear velocity of an entity's rigid body.
| arg | type | description |
|---|
| entityId | (string | entityRef | Target entity id or proxy; resolves from script context when omitted. |
examples
local v = Physics.getVelocity(id)
getAngularVelocity(entityId: (string | entityRef) →
Read the angular velocity of an entity's rigid body.
| arg | type | description |
|---|
| entityId | (string | entityRef | Target entity id or proxy; resolves from script context when omitted. |
examples
local w = Physics.getAngularVelocity(id)
isSleeping(entityId: (string | entityRef) →
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.
| arg | type | description |
|---|
| entityId | (string | entityRef | Target entity id or proxy; resolves from script context when omitted. |
examples
if Physics.isSleeping(id) then Physics.wakeUp(id) end
wakeUp(entityId: (string | entityRef) → void
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.
| arg | type | description |
|---|
| entityId | (string | entityRef | Target entity id or proxy; resolves from script context when omitted. |
examples
Physics.wakeUp(id)
idOf(ref: (string | entityRef) → void
Internal: the stable id of an entity id or proxy.
| arg | type | description |
|---|
| ref | (string | entityRef | |
observe(entityId: (string | entityRef, opts: ?) →
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.
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.
scene. `bodies` is an array, not a table keyed by entity id — each entry
names its own entity in `entity`.
| arg | type | description |
|---|
| entityId | (string | entityRef | Report on this one entity. Omit for every body in the world. |
| opts | ? | `{ bodies: boolean?, contactPoints: boolean? }` — `bodies = false` |
examples
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)
worldState( ) →
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.
examples
local w = Physics.worldState(); print(w.bodies.awake .. "/" .. w.bodies.total .. " awake")
print(Physics.worldState().contacts.touchingPairs .. " pairs touching")
bodyState(entityId: string | entityRef) →
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.
carries no rigid body — or `nil` when nothing in the scene answers to
that id.
| arg | type | description |
|---|
| entityId | string | entityRef | Entity id or proxy. |
examples
local b = Physics.bodyState(id); print(b.bodyType, b.mass, b.stillness)
if not Physics.bodyState(id).exists then print("no body was built") endwhyStill(entityId: string | entityRef) →
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.
| arg | type | description |
|---|
| entityId | string | entityRef | Entity id or proxy. |
examples
local why, detail = Physics.whyStill(id); if why then print(why, detail) end
stillnessReasons( ) →
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.
examples
for _, reason in Physics.stillnessReasons() do print(reason) end
contacts(entityId: string | entityRef) →
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.
or when the entity carries no rigid body.
| arg | type | description |
|---|
| entityId | string | entityRef | Entity id or proxy. |
examples
for _, c in Physics.contacts(id) do print(c.other, c.deepestPenetration) end
touching(entityId: string | entityRef, otherId: string | entityRef) →
Whether two entities are touching, and how deeply.
in metres and `0` for surfaces that meet without overlapping.
| arg | type | description |
|---|
| entityId | string | entityRef | Entity id or proxy. |
| otherId | string | entityRef | The other entity id or proxy. |
examples
local hit, depth = Physics.touching(a, b); print(hit, depth)
stepCost( ) →
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.
step — a paused simulation, or a world still bootstrapping.
examples
local c = Physics.stepCost(); if c then print(c.stepMs, c.narrowPhaseMs) end
applyForce(entityIdOrForce: string | entityRef | vec3, force: vec3?) → void
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.
| arg | type | description |
|---|
| entityIdOrForce | string | entityRef | vec3 | Entity id (when paired with `force`) OR a force vector for the script-context entity. |
| force | vec3? | Optional force vector when targeting an explicit entity. |
examples
Physics.applyForce({x=0, y=10, z=0})Physics.applyForce(entityId, {x=0, y=10, z=0})applyForceAtPoint(entityId: string | entityRef, force: vec3, point: vec3) → void
Apply a force at a specific world-space point — generates the
matching torque from the lever arm.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
| force | vec3 | Force vector. |
| point | vec3 | World-space application point. |
examples
Physics.applyForceAtPoint(id, {x=0,y=10,z=0}, {x=1,y=0,z=0})applyImpulse(entityIdOrImpulse: string | entityRef | vec3, impulse: vec3?) → void
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.
| arg | type | description |
|---|
| entityIdOrImpulse | string | entityRef | vec3 | Entity id (with `impulse`) OR an impulse vector for the script-context entity. |
| impulse | vec3? | Optional impulse vector when targeting an explicit entity. |
examples
Physics.applyImpulse({x=0, y=5, z=0})Physics.applyImpulse(entityId, {x=0, y=5, z=0})applyTorque(entityIdOrTorque: string | entityRef | vec3, torque: vec3?) → void
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.
| arg | type | description |
|---|
| entityIdOrTorque | string | entityRef | vec3 | Entity id (with `torque`) OR a torque vector for the script-context entity. |
| torque | vec3? | Optional torque vector when targeting an explicit entity. |
examples
Physics.applyTorque({x=0, y=1, z=0})Physics.applyTorque(entityId, {x=0, y=1, z=0})setVelocity(a: string | entityRef | number | vec3, b: (number | vec3, c: ?, d: ?) → void
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`.
| arg | type | description |
|---|
| a | string | entityRef | number | vec3 | x-component, a `{x, y, z}` vector, or an entity id (explicit target). |
| b | (number | vec3 | y-component, x-component, or the vector depending on call form. |
| c | ? | z-component or y-component depending on call form. |
| d | ? | Optional z-component when targeting an explicit entity. |
examples
Physics.setVelocity(0, 10, 0)
Physics.setVelocity(entityId, 0, 10, 0)
Physics.setVelocity(entityId, {x=0, y=10, z=0})addVelocity(a: string | entityRef | number | vec3, b: (number | vec3, c: ?, d: ?) → void
Add to the linear velocity of an entity. Same call shapes as
`setVelocity`.
| arg | type | description |
|---|
| a | string | entityRef | number | vec3 | dx, a `{x, y, z}` delta vector, or an entity id (explicit target). |
| b | (number | vec3 | dy, dx, or the delta vector depending on call form. |
| c | ? | dz or dy depending on call form. |
| d | ? | Optional dz when targeting an explicit entity. |
examples
Physics.addVelocity(0, 5, 0)
Physics.addVelocity(entityId, 0, 5, 0)
Physics.addVelocity(entityId, {x=0, y=5, z=0})setAngularVelocity(a: string | entityRef | number | vec3, b: (number | vec3, c: ?, d: ?) → void
Set the angular velocity of an entity (radians/sec). Same call
shapes as `setVelocity`.
| arg | type | description |
|---|
| a | string | entityRef | number | vec3 | x-component, a `{x, y, z}` vector, or an entity id (explicit target). |
| b | (number | vec3 | y-component, x-component, or the vector depending on call form. |
| c | ? | z-component or y-component depending on call form. |
| d | ? | Optional z-component when targeting an explicit entity. |
examples
Physics.setAngularVelocity(0, 0, 1)
Physics.setAngularVelocity(entityId, 0, 0, 1)
Physics.setAngularVelocity(entityId, {x=0, y=0, z=1})setGravityScale(entityIdOrScale: string | entityRef | number, scale: number?) → void
Set the per-entity gravity scale (1.0 = normal, 0.0 = no
gravity). One-arg form targets the script-context entity.
| arg | type | description |
|---|
| entityIdOrScale | string | entityRef | number | Entity id (with `scale`) OR scale value (script-context entity). |
| scale | number? | Optional explicit scale when targeting another entity. |
examples
Physics.setGravityScale(0.5)
Physics.setGravityScale(entityId, 0.5)
setMass(entityIdOrMass: string | entityRef | number, mass: number?) → void
Set the mass of an entity's rigid body (kg). One-arg form
targets the script-context entity.
| arg | type | description |
|---|
| entityIdOrMass | string | entityRef | number | Entity id (with `mass`) OR mass value (script-context entity). |
| mass | number? | Optional explicit mass when targeting another entity. |
examples
Physics.setMass(10)
Physics.setMass(entityId, 10)
setLinearDamping(entityIdOrDamping: string | entityRef | number, damping: number?) → void
Set linear damping on an entity's rigid body (0 = no damping).
One-arg form targets the script-context entity.
| arg | type | description |
|---|
| entityIdOrDamping | string | entityRef | number | Entity id (with `damping`) OR damping value (script-context entity). |
| damping | number? | Optional explicit damping when targeting another entity. |
examples
Physics.setLinearDamping(0.05)
Physics.setLinearDamping(entityId, 0.05)
setAngularDamping(entityIdOrDamping: string | entityRef | number, damping: number?) → void
Set angular damping on an entity's rigid body. One-arg form
targets the script-context entity.
| arg | type | description |
|---|
| entityIdOrDamping | string | entityRef | number | Entity id (with `damping`) OR damping value (script-context entity). |
| damping | number? | Optional explicit damping when targeting another entity. |
examples
Physics.setAngularDamping(0.1)
Physics.setAngularDamping(entityId, 0.1)
setCcdEnabled(entityIdOrEnabled: string | entityRef | boolean, enabled: boolean?) → void
Enable or disable continuous collision detection on an entity's
rigid body. One-arg form targets the script-context entity.
| arg | type | description |
|---|
| entityIdOrEnabled | string | entityRef | boolean | Entity id (with `enabled`) OR boolean (script-context entity). |
| enabled | boolean? | Optional explicit boolean when targeting another entity. |
examples
Physics.setCcdEnabled(true)
Physics.setCcdEnabled(entityId, true)
setBodyType(entityId: string | entityRef, bodyType: string) → void
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.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
| bodyType | string | One of `"dynamic"`, `"kinematic"`, `"static"`. |
examples
Physics.setBodyType(entityId, "kinematic")
setRotationLocks(entityId: string | entityRef, x: boolean, y: boolean, z: boolean) → void
Lock or unlock rotation on specific axes.
| arg | type | description |
|---|
| 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. |
examples
Physics.setRotationLocks(id, false, true, false)
setTranslationLocks(entityId: string | entityRef, x: boolean, y: boolean, z: boolean) → void
Lock or unlock translation on specific axes.
| arg | type | description |
|---|
| 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. |
examples
Physics.setTranslationLocks(id, false, false, true)
setCollisionGroups(entityId: string | entityRef, membership: number, filter: number) → void
Set the collision-group membership and filter bitmasks on an
entity's colliders. Adds a `CollisionGroup` component if missing.
| arg | type | description |
|---|
| 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. |
examples
Physics.setCollisionGroups(id, 0x0001, 0xFFFF)
ignoreCollision(entityIdA: string | entityRef, entityIdB: string | entityRef, ignore: boolean?) → void
Toggle ignored-collision state between two specific entities.
| arg | type | description |
|---|
| entityIdA | string | entityRef | First entity id. |
| entityIdB | string | entityRef | Second entity id. |
| ignore | boolean? | When `true` (default) collisions between the pair are skipped. |
examples
Physics.ignoreCollision(a, b, true)
addJoint(entityIdA: string | entityRef, entityIdB: string | entityRef, opts: table?) → void
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`).
| arg | type | description |
|---|
| entityIdA | string | entityRef | Entity that hosts the Joint component. |
| entityIdB | string | entityRef | Connected entity. |
| opts | table? | Optional joint description (kind, anchors, axis, stiffness, damping, restLength, maxDistance, breakForce, breakTorque). |
examples
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 })comp(prefix: ?, vec: ?) → void
Joint's schema is split-form (localAnchorX/Y/Z, etc.). Accept
either the vec3 table form callers naturally write OR the
pre-split keys that phys.addJoint emits, and always forward
as split keys so component.add doesn't reject anything.
| arg | type | description |
|---|
| prefix | ? | |
| vec | ? | |
removeJoint(entityId: string | entityRef) → void
Remove the Joint component from an entity (if present).
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
examples
Physics.removeJoint(id)
setJointMotor(entityId: string | entityRef, targetVelocity: number, maxForce: number) → void
Set a motor on an entity's joint.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id (must carry a Joint component). |
| targetVelocity | number | Desired joint velocity. |
| maxForce | number | Maximum force the motor can apply. |
examples
Physics.setJointMotor(id, 5.0, 1000)
pumpJointBreaks( ) → void
The engine hands each break over exactly once, so every record this reads is
the only copy there will ever be: buffer the whole batch before any listener
runs, and run each listener under pcall, so one handler that raises costs its
own delivery and nothing else's.
pumpJointBreaks( ) → void
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.
examples
Physics.pumpJointBreaks()
jointReaction(entityId: string | entityRef) →
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.
the entity owns no joint.
| arg | type | description |
|---|
| entityId | string | entityRef | Entity carrying the Joint component. |
examples
local r = Physics.jointReaction(id); print(r and r.force)
jointBreaks( ) →
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.
examples
for _, e in ipairs(Physics.jointBreaks()) do print(e.entityId, e.force) end
onJointBreak(fn: (table) →
Call `fn` for every joint that breaks from now on, with the same record
`jointBreaks` returns.
| arg | type | description |
|---|
| fn | (table | Receives one break record per broken joint. |
examples
local off = Physics.onJointBreak(function(e) print(e.kind, e.force, e.position) end)
addConstraint(entityId: string | entityRef, opts: table?) → void
Add a transform constraint to an entity.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
| opts | table? | Optional constraint description (targetEntityId, position, rotation, scale, lookAt, targetPosition, axes, weight). |
examples
Physics.addConstraint(id, { targetEntityId = parent, position = true })removeConstraint(entityId: string | entityRef, index: number?) → void
Remove transform constraints from an entity (if any are
present).
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
| index | number? | Optional constraint index (currently ignored — the whole component is removed). |
examples
Physics.removeConstraint(id)
colliderOn(entityId: string | entityRef) → string
Which collider component an entity carries, or nil when it carries none.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
examples
local which = Physics.colliderOn(id)
addCollider(entityId: string | entityRef, component: string, config: table?) → void
Add a collider component to an entity, naming the shape you want.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
| component | string | One of `Physics.COLLIDER_COMPONENTS`. |
| config | table? | The component's own fields, e.g. `{ radius = 0.5 }` for a sphere. |
examples
Physics.addCollider(id, "SphereCollider", { radius = 0.5 })removeCollider(entityId: string | entityRef) → string
Remove whichever collider component an entity carries.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
examples
Physics.removeCollider(id)
addWheelCollider(entityId: string | entityRef, config: table?) → void
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.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
| config | table? | Optional wheel configuration (`radius?`, `suspensionDistance?`, `springRate?`, `damperRate?`, `motorTorque?`, `brakeTorque?`, `steerAngle?`, `forwardFriction?`, `sidewaysFriction?`, `is2D?`). |
examples
Physics.addWheelCollider(id, { radius = 0.35, motorTorque = 500 })removeWheelCollider(entityId: string | entityRef) → void
Remove the WheelCollider component from an entity (if present).
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id. |
examples
Physics.removeWheelCollider(id)
getWheelState(entityId: string | entityRef) →
Read a wheel collider's runtime state. Reads the native component
the wheel system writes after each physics step.
| arg | type | description |
|---|
| entityId | string | entityRef | Target entity id (must carry a WheelCollider component). |
examples
local state = Physics.getWheelState(id)
raycastBetween(fromId: string, toId: string, maxDistance: number?) →
Cast a ray from one entity toward another and return the first
hit.
| arg | type | description |
|---|
| fromId | string | Origin entity id. |
| toId | string | Target entity id. |
| maxDistance | number? | Optional distance cap (default 1000). |
examples
local hit = Physics.raycastBetween(a, b)
hasLineOfSight(fromId: string, toId: string) →
Check whether two entities have line-of-sight between their
origins.
| arg | type | description |
|---|
| fromId | string | Viewer entity id. |
| toId | string | Target entity id. |
examples
if Physics.hasLineOfSight(a, b) then ... end