Skip to content

Physics

Physics makes things fall, collide and push each other. It runs on Jolt, at 60 steps a second, and only while the game plays or a scene page simulates; in edit mode nothing moves.

Give an entity Add Component > Rigid Body to make it solid. Its Motion decides who moves it:

  • Static: never moves. Floors, walls, level geometry.
  • Kinematic: moved by you, from a script or an animation, and pushes dynamic bodies out of its way. Moving platforms and doors. Set its position as you would any entity’s.
  • Dynamic: moved by physics, falling and bouncing. Crates, balls, debris. While the game plays, physics owns its position; setting it from a script has no effect.

Its Shape is one of:

Shape Size from Notes
Box Half Extents Half the box’s size along each axis: (0.5, 0.5, 0.5) is a 1 m cube.
Sphere Radius
Capsule Radius, Half Height Half Height is half the length of the straight middle.
Cooked Collision Shape A shape made from a mesh (below).
Plane Plane Half Extent The entity’s flat XZ plane, solid below. Static only.
Heightfield Heightfield Terrain ground (below). Static only.

Box, sphere and capsule sizes ignore the entity’s scale: size them with their fields. A cooked shape does follow the scale.

The other settings:

  • Friction (0.5) and Restitution (0, no bounce), or a Material (below) that sets both.
  • Linear Damping and Angular Damping: how quickly motion and spin die away.
  • Mass: in kilograms. 0, the default, works it out from the shape’s size.
  • Continuous Collision: for small, fast dynamic bodies that would otherwise pass through thin walls. It costs a little more, so turn it on only where needed.
  • Is Trigger, Layer and Collision Group: see below.

A body is built when the game starts, and again when its entity is turned off and on. Changing its shape or motion while the game runs has no effect until then.

A body’s shape can be made of several. Add Collider components to child entities of the body: each adds its shape, where the child is, to the nearest rigid body above it. A table is a rigid body with a box for the top and a child collider for each leg.

For a shape that follows a model, make a Collision Shape asset (Create > Physics > Collision Shape), set its Source Mesh, and choose how it is cooked:

  • ConvexHull: a simplified wrapping, with no dents or holes. It can move, so use it for dynamic bodies.
  • TriangleMesh: the mesh exactly, for static level geometry only.

Then set the body’s Shape to Cooked and its Collision Shape to the asset. Importing a model with Generate collision on does all of this for each of its static meshes, and gives the model’s prefab a static body (Convex collision chooses hulls instead).

A Physical Material asset (Create > Physics) holds a Friction, a Restitution and a Density (1000 kg per cubic metre, like water, by default). Set it as a body’s Material to share the values between bodies; it overrides the body’s own friction and restitution. A ray that hits a body can tell which material it hit, to pick a footstep sound.

A body with Is Trigger on collides with nothing: it notices what enters and leaves it. Use one for pickups, checkpoints, and areas that start something. Its scripts hear onTriggerEnter and onTriggerExit, and so do the scripts on whatever entered.

Two settings filter collisions.

Layer says what kind of body it is: Static, Dynamic, Kinematic or Trigger. Static bodies never collide with each other, nor triggers with each other or with static bodies; everything else does. Keep the layer the same as the motion.

Collision Group (0 to 31) is yours to define. In the scene’s inspector, on its Scene tab, the Physics section has a Collision Groups grid: name the groups, and untick the pairs that should pass through each other, such as the player and its own bullets. By default every group collides with every other.

The same section has the scene’s Gravity (0, -9.81, 0), Collision Steps (more steps per frame make fast stacks steadier, at a cost), and Debug Draw, which outlines every body as the game plays: green when moving, grey when asleep, blue for static and kinematic bodies, yellow for triggers.

When two bodies touch, both entities’ behaviors are called, each told about the other:

function Crate:onContactBegin(other, point, normal, speed)
if speed > 2.0 then
Log.info("hit " .. other:name() .. " hard")
end
end
function Crate:onContactEnd(other, point, normal, speed)
end
function Checkpoint:onTriggerEnter(other)
other:send("Checkpoint")
end
function Checkpoint:onTriggerExit(other)
end

speed is how fast the two came together, which tells a tap from a crash. point is where they touched. On onContactEnd the three values are zero.

These calls arrive after the physics step, never in the middle of it, so a script may change anything in them: destroy the entity, spawn another, move a body.

ScenePhysics asks the world questions and pushes bodies:

local physics = ScenePhysics.of(self.entity.scene)
-- Straight down, up to 2 m: is there ground?
local hit = physics:rayCast(self.entity:worldPosition(), Float3.new(0, -1, 0), 2.0)
if hit.hit then
Log.info("ground " .. hit.distance .. " m below, on " .. hit:entity():name())
end
-- Everything within 3 m
for _, near in ipairs(physics:overlapSphere(self.entity:worldPosition(), 3.0)) do
physics:applyImpulse(near, Float3.new(0, 4, 0))
end
  • rayCast(from, direction, distance) finds the first body along a line. Give it a direction of length 1. The hit has hit, distance, position, normal, entity() and material().
  • sphereCast(from, direction, distance, radius) does the same with a ball, for a wider probe.
  • overlapSphere(center, radius) lists every entity within the sphere; nearestOverlap gives the nearest.
  • Each takes an optional last argument, a group mask, to look only at some collision groups: bit g set means group g counts.
  • applyImpulse(entity, impulse) kicks a dynamic body: a jump, an explosion, a hit.
  • setVelocity(entity, velocity) sets its speed and direction at once, in metres a second: a launch, a dash, a sudden stop. velocity(entity) reads it.
  • setForce(entity, force) holds a steady push on a dynamic body, in newtons, every moment until you set it again: thrust, wind, a current. Set it to zero to let go. Because it is held, it pushes the same at any frame rate; don’t set it again every frame.
  • An impulse or a velocity given to a body spawned this frame waits until the body exists.
  • setGravity changes the scene’s gravity while it plays.

Queries see triggers too.

A body’s settings can be read from a script through RigidBodyComponent.of(entity) (RigidBodyComponent::of in AngelScript), for example its mass.

A Joint component ties its entity’s rigid body to another body, or to the world:

Kind Allows
Fixed Nothing: the two move as one.
Point Swinging in any direction about the anchor: a chain, a hanging lamp.
Hinge Turning about one axis: a door, a wheel.
Slider Sliding along one axis: a drawer, a piston.
Distance Moving freely within a range of distances: a rope.

The body it ties to is Target Entity; left empty, it is the nearest rigid body above, or the world if there is none. Local Anchor is the pivot and Local Axis the hinge or slider axis, both in the entity’s own space.

Limit Min and Limit Max bound a hinge, in radians, or a slider, in metres; a minimum above the maximum means no limit. A hinge or slider can also have a motor: turn on Motor Enabled and set its Motor Target Velocity and the most force or torque it may use, Motor Limit. A Distance joint uses Min Distance and Max Distance instead; left negative, they keep the distance the bodies start at.

A player or a walking enemy wants to walk up slopes and steps, stand on moving floors, and push crates, without tipping over. That is the Character component: a capsule moved by your script, not by forces. Give the entity a Character instead of a rigid body.

  • Radius (0.35) and Half Height (0.55) size the capsule; its height is twice their sum, 1.8 m. The entity’s position is the capsule’s centre.
  • Max Slope Degrees (50) is the steepest ground it can stand on.
  • Step Up (0.4) and Step Down (0.5) are the steps it walks up and down without leaving the ground.
  • Mass (70) and Max Strength (500) decide how hard it pushes bodies.

Its script sets the speed it should move at, every frame:

function Player:onUpdate(dt)
local character = CharacterComponent.of(self.entity)
local move = Input.value2D("Move")
character:move(move.x * 5.0, -move.y * 5.0)
if character:grounded() and Input.wasPressed("Jump") then
character:jump(6.0)
end
end
  • move(x, z) sets the speed across the ground, in metres a second; gravity does the rest.
  • jump(speed) jumps the next time it is on the ground; launch(speed) sets its upward speed even in the air, for a double jump or a spring. A negative speed slams it down.
  • drive(velocity) hands the whole motion, gravity included, to the script, for flying or swimming, until the next move.
  • setPosition(position) moves it at once, to respawn it. Use this, not the entity’s own setPosition, which physics would overwrite.
  • grounded(), velocity and groundNormal say how it is doing.

A character sets off triggers, but it is not a body: contacts with it are not reported, and rays and overlaps do not see it.

A terrain is not solid by itself. To walk on it, give the terrain’s entity a Rigid Body with Motion Static, Shape Heightfield and its Heightfield set to the terrain’s heightfield. Holes cut in the terrain stay open. Vegetation that has a Collision Radius is solid by itself.