Physics
The Physics namespace — the engine's Luau API reference for Physics.
The Physics namespace — 58 functions.
globals/Physics/addCollider
Physics.addCollider(entityId: string | entityProxy, component: string, config: table?)
Add a collider component to an entity, naming the shape you want.
Parameters
entityIdstring | entityProxy— Target entity id.componentstring— One ofPhysics.COLLIDER_COMPONENTS.configtable(optional) — The component's own fields, e.g.{ radius = 0.5 }for a sphere.
Physics.addCollider(id, "SphereCollider", { radius = 0.5 })
globals/Physics/addConstraint
Physics.addConstraint(entityId: string | entityProxy, opts: table?)
Add a transform constraint to an entity.
Parameters
entityIdstring | entityProxy— Target entity id.optstable(optional) — Optional constraint description (targetEntityId, position, rotation, scale, lookAt, targetPosition, axes, weight).
Physics.addConstraint(id, { targetEntityId = parent, position = true })
globals/Physics/addJoint
Physics.addJoint(entityIdA: string | entityProxy, entityIdB: string | entityProxy, 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
entityIdAstring | entityProxy— Entity that hosts the Joint component.entityIdBstring | entityProxy— Connected entity.optstable(optional) — Optional joint description (kind, anchors, axis, stiffness, damping, restLength, maxDistance, breakForce, breakTorque).
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
Physics.addVelocity(a: string | entityProxy | number | vec3, b: (number | vec3)?, c: number?, d: number?)
Add to the linear velocity of an entity. Same call shapes as
setVelocity.
Parameters
astring | entityProxy | 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.cnumber(optional) — dz or dy depending on call form.dnumber(optional) — Optional dz when targeting an explicit entity.
Physics.addVelocity(0, 5, 0)
Physics.addVelocity(entityId, 0, 5, 0)
Physics.addVelocity(entityId, {x=0, y=5, z=0})
globals/Physics/addWheelCollider
Physics.addWheelCollider(entityId: string | entityProxy, 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
entityIdstring | entityProxy— Target entity id.configtable(optional) — Optional wheel configuration (radius?,suspensionDistance?,springRate?,damperRate?,motorTorque?,brakeTorque?,steerAngle?,forwardFriction?,sidewaysFriction?,is2D?).
Physics.addWheelCollider(id, { radius = 0.35, motorTorque = 500 })
globals/Physics/applyForce
Physics.applyForce(entityIdOrForce: string | entityProxy | 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
entityIdOrForcestring | entityProxy | vec3— Entity id (when paired withforce) OR a force vector for the script-context entity.forcevec3(optional) — Optional force vector when targeting an explicit entity.
Physics.applyForce({x=0, y=10, z=0})
Physics.applyForce(entityId, {x=0, y=10, z=0})
globals/Physics/applyForceAtPoint
Physics.applyForceAtPoint(entityId: string | entityProxy, force: vec3, point: vec3)
Apply a force at a specific world-space point — generates the matching torque from the lever arm.
Parameters
entityIdstring | entityProxy— Target entity id.forcevec3— Force vector.pointvec3— World-space application point.
Physics.applyForceAtPoint(id, {x=0,y=10,z=0}, {x=1,y=0,z=0})
globals/Physics/applyImpulse
Physics.applyImpulse(entityIdOrImpulse: string | entityProxy | 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
entityIdOrImpulsestring | entityProxy | vec3— Entity id (withimpulse) OR an impulse vector for the script-context entity.impulsevec3(optional) — Optional impulse vector when targeting an explicit entity.
Physics.applyImpulse({x=0, y=5, z=0})
Physics.applyImpulse(entityId, {x=0, y=5, z=0})
globals/Physics/applyTorque
Physics.applyTorque(entityIdOrTorque: string | entityProxy | 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
entityIdOrTorquestring | entityProxy | vec3— Entity id (withtorque) OR a torque vector for the script-context entity.torquevec3(optional) — Optional torque vector when targeting an explicit entity.
Physics.applyTorque({x=0, y=1, z=0})
Physics.applyTorque(entityId, {x=0, y=1, z=0})
globals/Physics/bodyState
Physics.bodyState(entityId: string | entityProxy) -> 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
entityIdstring | entityProxy— 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.
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
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
originvec3— Box center at the start of the cast.halfExtentsvec3— Half the size of the box on each axis.directionvec3— Cast direction.maxDistancenumber(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.
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
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
originvec3— Capsule centre at the start of the cast.radiusnumber— Capsule radius.halfHeightnumber— Distance from the centre to either cap centre. The capsule standshalfHeight + radiustall in each direction.directionvec3— Cast direction.maxDistancenumber(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.
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/colliderGeometry
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
optionstable(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 }.
local geo = Physics.colliderGeometry({ tessellation = "high" })
globals/Physics/colliderManifest
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 }.
for _, c in ipairs(Physics.colliderManifest()) do print(c.entity, c.role) end
globals/Physics/colliderOn
Physics.colliderOn(entityId: string | entityProxy) -> string?
Which collider component an entity carries, or nil when it carries none.
Parameters
entityIdstring | entityProxy— Target entity id.
Returns string? The component name, e.g. "SphereCollider".
local which = Physics.colliderOn(id)
globals/Physics/colliderShapes
Physics.colliderShapes(entityId: string | entityProxy) -> 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
entityIdstring | entityProxy— Target entity id.
Returns table — Array of resolved collider shapes (empty if none): { shapeType, position, rotation, params, linePoints, name? }.
local shapes = Physics.colliderShapes(id)
globals/Physics/contacts
Physics.contacts(entityId: string | entityProxy) -> { 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
entityIdstring | entityProxy— Entity id or proxy.
Returns { PhysicsContact } — An array of PhysicsContact — empty when the body touches nothing, or when the entity carries no rigid body.
for _, c in Physics.contacts(id) do print(c.other, c.deepestPenetration) end
globals/Physics/getAngularVelocity
Physics.getAngularVelocity(entityId: (string | entityProxy)?) -> vec3?
Read the angular velocity of an entity's rigid body.
Parameters
entityId(string | entityProxy)(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.
local w = Physics.getAngularVelocity(id)
globals/Physics/getGravity
Physics.getGravity() -> vec3
Read the current world gravity vector.
Returns vec3 — Gravity vector in m/s² (negative y is "down" in the default world).
local g = Physics.getGravity()
globals/Physics/getVelocity
Physics.getVelocity(entityId: (string | entityProxy)?) -> vec3?
Read the linear velocity of an entity's rigid body.
Parameters
entityId(string | entityProxy)(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.
local v = Physics.getVelocity(id)
globals/Physics/getWheelState
Physics.getWheelState(entityId: string | entityProxy) -> table?
Read a wheel collider's runtime state. Reads the native component the wheel system writes after each physics step.
Parameters
entityIdstring | entityProxy— Target entity id (must carry a WheelCollider component).
Returns table? — { isGrounded, compression, angularVelocity }, or nil if the component is absent.
local state = Physics.getWheelState(id)
globals/Physics/hasLineOfSight
Physics.hasLineOfSight(fromId: string, toId: string) -> boolean
Check whether two entities have line-of-sight between their origins.
Parameters
fromIdstring— Viewer entity id.toIdstring— Target entity id.
Returns boolean — true when no collider sits between them (including coincident origins), false otherwise.
if Physics.hasLineOfSight(a, b) then ... end
globals/Physics/ignoreCollision
Physics.ignoreCollision(entityIdA: string | entityProxy, entityIdB: string | entityProxy, ignore: boolean?)
Toggle ignored-collision state between two specific entities.
Parameters
entityIdAstring | entityProxy— First entity id.entityIdBstring | entityProxy— Second entity id.ignoreboolean(optional) — Whentrue(default) collisions between the pair are skipped.
Physics.ignoreCollision(a, b, true)
globals/Physics/isSleeping
Physics.isSleeping(entityId: (string | entityProxy)?) -> 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 | entityProxy)(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.
if Physics.isSleeping(id) then Physics.wakeUp(id) end
globals/Physics/jointBreaks
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.
for _, e in ipairs(Physics.jointBreaks()) do print(e.entityId, e.force) end
globals/Physics/jointReaction
Physics.jointReaction(entityId: string | entityProxy) -> 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
entityIdstring | entityProxy— Entity carrying the Joint component.
Returns table? — { impulse, angularImpulse, force, torque, position }, or nil when the entity owns no joint.
local r = Physics.jointReaction(id); print(r and r.force)
globals/Physics/observe
Physics.observe(entityId: (string | entityProxy)?, 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 | entityProxy)(optional) — Report on this one entity. Omit for every body in the world.optstable(optional) —{ bodies: boolean?, contactPoints: boolean? }—bodies = falsebuilds the world accounting alone, andcontactPoints = falsekeeps 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.
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
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.
local off = Physics.onJointBreak(function(e) print(e.kind, e.force, e.position) end)
globals/Physics/overlapSphere
Physics.overlapSphere(center: vec3, radius: number) -> table
Find every entity id whose colliders overlap a sphere.
Parameters
centervec3— Sphere center in world space.radiusnumber— Sphere radius.
Returns table — Array of overlapping entity ids.
local ids = Physics.overlapSphere({x=0,y=0,z=0}, 5)
globals/Physics/pumpJointBreaks
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.
Physics.pumpJointBreaks()
globals/Physics/raycast
Physics.raycast(origin: vec3, direction: vec3, maxDistance: number?, exclude: (string | {string})?) -> table?
Cast a ray and return the first hit.
Parameters
originvec3— Ray origin in world space.directionvec3— Ray direction (does not need to be unit-length; the engine normalises).maxDistancenumber(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.
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 })
globals/Physics/raycastAll
Physics.raycastAll(origin: vec3, direction: vec3, maxDistance: number?, maxHits: number?, exclude: (string | {string})?) -> table
Cast a ray and return every hit up to maxHits.
Parameters
originvec3— Ray origin in world space.directionvec3— Ray direction.maxDistancenumber(optional) — Optional distance limit along the ray.maxHitsnumber(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.
local hits = Physics.raycastAll(origin, dir, 50, 4)
globals/Physics/raycastBetween
Physics.raycastBetween(fromId: string, toId: string, maxDistance: number?) -> table?
Cast a ray from one entity toward another and return the first hit.
Parameters
fromIdstring— Origin entity id.toIdstring— Target entity id.maxDistancenumber(optional) — Optional distance cap (default 1000).
Returns table? — Hit table, or nil if the entities are coincident or nothing was hit.
local hit = Physics.raycastBetween(a, b)
globals/Physics/raycastScreen
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
sxnumber— Screen X in viewport-local pixels (the space ofinput.mouse_positionandscreenToRay).synumber— Screen Y in viewport-local pixels.maxDistancenumber(optional) — Maximum distance along the ray (defaults to 1000, matchingraycast).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.
local m = input.mouse_position; local hit = Physics.raycastScreen(m[1], m[2])
globals/Physics/removeCollider
Physics.removeCollider(entityId: string | entityProxy) -> string?
Remove whichever collider component an entity carries.
Parameters
entityIdstring | entityProxy— Target entity id.
Returns string? The component that was removed, or nil when there was none.
Physics.removeCollider(id)
globals/Physics/removeConstraint
Physics.removeConstraint(entityId: string | entityProxy, index: number?)
Remove transform constraints from an entity (if any are present).
Parameters
entityIdstring | entityProxy— Target entity id.indexnumber(optional) — Optional constraint index (currently ignored — the whole component is removed).
Physics.removeConstraint(id)
globals/Physics/removeJoint
Physics.removeJoint(entityId: string | entityProxy)
Remove the Joint component from an entity (if present).
Parameters
entityIdstring | entityProxy— Target entity id.
Physics.removeJoint(id)
globals/Physics/removeWheelCollider
Physics.removeWheelCollider(entityId: string | entityProxy)
Remove the WheelCollider component from an entity (if present).
Parameters
entityIdstring | entityProxy— Target entity id.
Physics.removeWheelCollider(id)
globals/Physics/setAngularDamping
Physics.setAngularDamping(entityIdOrDamping: string | entityProxy | number, damping: number?)
Set angular damping on an entity's rigid body. One-arg form targets the script-context entity.
Parameters
entityIdOrDampingstring | entityProxy | number— Entity id (withdamping) OR damping value (script-context entity).dampingnumber(optional) — Optional explicit damping when targeting another entity.
Physics.setAngularDamping(0.1)
Physics.setAngularDamping(entityId, 0.1)
globals/Physics/setAngularVelocity
Physics.setAngularVelocity(a: string | entityProxy | number | vec3, b: (number | vec3)?, c: number?, d: number?)
Set the angular velocity of an entity (radians/sec). Same call
shapes as setVelocity.
Parameters
astring | entityProxy | 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.cnumber(optional) — z-component or y-component depending on call form.dnumber(optional) — Optional z-component when targeting an explicit entity.
Physics.setAngularVelocity(0, 0, 1)
Physics.setAngularVelocity(entityId, 0, 0, 1)
Physics.setAngularVelocity(entityId, {x=0, y=0, z=1})
globals/Physics/setBodyType
Physics.setBodyType(entityId: string | entityProxy, 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
entityIdstring | entityProxy— Target entity id.bodyTypestring— One of"dynamic","kinematic","static".
Physics.setBodyType(entityId, "kinematic")
globals/Physics/setCcdEnabled
Physics.setCcdEnabled(entityIdOrEnabled: string | entityProxy | boolean, enabled: boolean?)
Enable or disable continuous collision detection on an entity's rigid body. One-arg form targets the script-context entity.
Parameters
entityIdOrEnabledstring | entityProxy | boolean— Entity id (withenabled) OR boolean (script-context entity).enabledboolean(optional) — Optional explicit boolean when targeting another entity.
Physics.setCcdEnabled(true)
Physics.setCcdEnabled(entityId, true)
globals/Physics/setCollisionGroups
Physics.setCollisionGroups(entityId: string | entityProxy, membership: number, filter: number)
Set the collision-group membership and filter bitmasks on an
entity's colliders. Adds a CollisionGroup component if missing.
Parameters
entityIdstring | entityProxy— Target entity id.membershipnumber— Bitmask: which groups this collider belongs to.filternumber— Bitmask: which groups this collider can collide with.
Physics.setCollisionGroups(id, 0x0001, 0xFFFF)
globals/Physics/setGravity
Physics.setGravity(gravity: vec3)
Replace the world gravity vector.
Parameters
gravityvec3— New gravity vector in m/s².
Physics.setGravity({x=0, y=-9.81, z=0})
globals/Physics/setGravityScale
Physics.setGravityScale(entityIdOrScale: string | entityProxy | 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
entityIdOrScalestring | entityProxy | number— Entity id (withscale) OR scale value (script-context entity).scalenumber(optional) — Optional explicit scale when targeting another entity.
Physics.setGravityScale(0.5)
Physics.setGravityScale(entityId, 0.5)
globals/Physics/setJointMotor
Physics.setJointMotor(entityId: string | entityProxy, targetVelocity: number, maxForce: number)
Set a motor on an entity's joint.
Parameters
entityIdstring | entityProxy— Target entity id (must carry a Joint component).targetVelocitynumber— Desired joint velocity.maxForcenumber— Maximum force the motor can apply.
Physics.setJointMotor(id, 5.0, 1000)
globals/Physics/setLinearDamping
Physics.setLinearDamping(entityIdOrDamping: string | entityProxy | number, damping: number?)
Set linear damping on an entity's rigid body (0 = no damping). One-arg form targets the script-context entity.
Parameters
entityIdOrDampingstring | entityProxy | number— Entity id (withdamping) OR damping value (script-context entity).dampingnumber(optional) — Optional explicit damping when targeting another entity.
Physics.setLinearDamping(0.05)
Physics.setLinearDamping(entityId, 0.05)
globals/Physics/setMass
Physics.setMass(entityIdOrMass: string | entityProxy | number, mass: number?)
Set the mass of an entity's rigid body (kg). One-arg form targets the script-context entity.
Parameters
entityIdOrMassstring | entityProxy | number— Entity id (withmass) OR mass value (script-context entity).massnumber(optional) — Optional explicit mass when targeting another entity.
Physics.setMass(10)
Physics.setMass(entityId, 10)
globals/Physics/setRotationLocks
Physics.setRotationLocks(entityId: string | entityProxy, x: boolean, y: boolean, z: boolean)
Lock or unlock rotation on specific axes.
Parameters
entityIdstring | entityProxy— Target entity id.xboolean— Lock rotation about the world X axis.yboolean— Lock rotation about the world Y axis.zboolean— Lock rotation about the world Z axis.
Physics.setRotationLocks(id, false, true, false)
globals/Physics/setTranslationLocks
Physics.setTranslationLocks(entityId: string | entityProxy, x: boolean, y: boolean, z: boolean)
Lock or unlock translation on specific axes.
Parameters
entityIdstring | entityProxy— Target entity id.xboolean— Lock translation along the world X axis.yboolean— Lock translation along the world Y axis.zboolean— Lock translation along the world Z axis.
Physics.setTranslationLocks(id, false, false, true)
globals/Physics/setVelocity
Physics.setVelocity(a: string | entityProxy | 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
astring | entityProxy | 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.cnumber(optional) — z-component or y-component depending on call form.dnumber(optional) — Optional z-component when targeting an explicit entity.
Physics.setVelocity(0, 10, 0)
Physics.setVelocity(entityId, 0, 10, 0)
Physics.setVelocity(entityId, {x=0, y=10, z=0})
globals/Physics/sphereCast
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
originvec3— Sphere center at the start of the cast.radiusnumber— Sphere radius.directionvec3— Cast direction.maxDistancenumber(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.
local hit = Physics.sphereCast(o, 0.5, dir, 10)
local hit = Physics.sphereCast(o, 0.5, dir, 10, selfId)
globals/Physics/stepCost
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.
local c = Physics.stepCost(); if c then print(c.stepMs, c.narrowPhaseMs) end
globals/Physics/stillnessReasons
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.
for _, reason in Physics.stillnessReasons() do print(reason) end
globals/Physics/touching
Physics.touching(entityId: string | entityProxy, otherId: string | entityProxy) -> (boolean, number, { PhysicsContactPoint })
Whether two entities are touching, and how deeply.
Parameters
entityIdstring | entityProxy— Entity id or proxy.otherIdstring | entityProxy— 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.
local hit, depth = Physics.touching(a, b); print(hit, depth)
globals/Physics/wakeUp
Physics.wakeUp(entityId: (string | entityProxy)?)
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 | entityProxy)(optional) — Target entity id or proxy; resolves from script context when omitted.
Physics.wakeUp(id)
globals/Physics/whyStill
Physics.whyStill(entityId: string | entityProxy) -> (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
entityIdstring | entityProxy— Entity id or proxy.
Returns (string?, string?) — (reason, detail).
local why, detail = Physics.whyStill(id); if why then print(why, detail) end
globals/Physics/worldState
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.
local w = Physics.worldState(); print(w.bodies.awake .. "/" .. w.bodies.total .. " awake")
print(Physics.worldState().contacts.touchingPairs .. " pairs touching")