3D Physics (Bullet 3D)¶
3D Physics in Codea¶
Codea’s 3D physics system is built on the Bullet physics engine. It provides rigid body dynamics with support for forces, impulses, joints, and collision events.
3D physics can be used in two ways: standalone with physics3d.world, or integrated with the scene system via entity components.
Creating Physics Worlds¶
A standalone physics world manages its own simulation loop:
function setup()
world = physics3d.world()
world.gravity = vec3(0, -9.8, 0)
-- Create a ground plane
ground = world:body(physics3d.STATIC)
ground:add(physics3d.plane(vec3(0, 1, 0), 0))
-- Create a dynamic sphere
ball = world:body(physics3d.DYNAMIC)
ball:add(physics3d.sphere(0.5))
ball.position = vec3(0, 5, 0)
end
function update(dt)
world:update(dt)
end
function draw()
-- Draw the ball position
pushMatrix()
translate(ball.position.x, ball.position.y, ball.position.z)
sphere(0.5)
popMatrix()
end
Body types:
physics3d.STATIC— does not move, acts as immovable sceneryphysics3d.DYNAMIC— moved by forces and collisionsphysics3d.KINEMATIC— moved manually via code, pushes dynamic bodies without being affected by gravity
Using Physics with Entities¶
When working with a scene, attach physics components directly to entities:
function setup()
scn = scene()
local cam = scn:entity("camera")
cam:add(camera.perspective(60))
cam:add(camera.rigs.orbit)
cam.z = -10
-- Static ground
local ground = scn:entity("ground")
ground:add(mesh.plane())
ground:add(physics3d.body(physics3d.STATIC))
ground:add(physics3d.box(vec3(10, 0.1, 10)))
ground.material = material.lit()
-- Dynamic box
local box = scn:entity("box")
box:add(mesh.box())
box:add(physics3d.body(physics3d.DYNAMIC))
box:add(physics3d.box(vec3(1, 1, 1)))
box.y = 5
box.material = material.lit()
scene.main = scn
end
The scene automatically steps the physics simulation each frame.
Collision Shapes¶
Choose the collision shape that best matches your object’s visual appearance. Simpler shapes perform better:
physics3d.sphere(radius)— spherephysics3d.box(halfExtents)— axis-aligned box, parameter is half-widths as a vec3physics3d.capsule(radius, height)— capsule (good for characters)physics3d.cylinder(radius, height)— cylinderphysics3d.cone(radius, height)— conephysics3d.plane(normal, constant)— infinite plane, STATIC onlyphysics3d.hull(points)— convex hull around a set of points
body:add(physics3d.sphere(1))
body:add(physics3d.box(vec3(0.5, 1, 0.5)))
body:add(physics3d.capsule(0.4, 1.8))
Collision Callbacks¶
React to collisions through entity callbacks or body callbacks:
local box = scn:entity()
box:add(physics3d.body(physics3d.DYNAMIC))
box:add(physics3d.box(vec3(1,1,1)))
box.collisionBegan3d = function(contact)
print("Hit something!", contact.impulse)
end
The contact object contains impulse (force magnitude) and normal (collision normal vector).
Forces and Impulses¶
Apply forces and impulses to dynamic bodies to set them in motion:
local body = ent:get(physics3d.body)
-- Continuous force (applied each frame, use in update)
body:applyForce(vec3(0, 100, 0))
-- Instant impulse (applied once)
body:applyImpulse(vec3(0, 10, 0))
-- Torque (rotational force)
body:applyTorque(vec3(0, 5, 0))
-- Set velocity directly
body.linearVelocity = vec3(1, 0, 0)
body.angularVelocity = vec3(0, 1, 0)