-- Newton's cradle: array of balls suspended from two strings, demonstrating conservation of energy / momentum -- Strings are modeled with distance joints, which means they behave more like rods. local world local frame local framePose local balls = {} local count = 10 local radius = 1 / count / 2 -- small air gap between balls results in collisions in separate frames, to carry impulse through to last ball -- without this gap the physics engine would need to calculate transfer of impulses between contacts local gap = 0.01 function lovr.load() world = lovr.physics.newWorld({ restitutionThreshold = .05 }) -- a static geometry from which balls are suspended local size = vec3(1.2, 0.1, 0.3) frame = world:newBoxCollider(vec3(0, 2, -2), size) frame:setKinematic(true) framePose = lovr.math.newMat4(frame:getPose()):scale(size) -- create balls along the length of frame and attach them with two distance joints to frame for x = -0.5, 0.5, 1 / count do local ball = world:newSphereCollider(vec3(x, 1, -2), radius - gap) ball:setRestitution(1.0) table.insert(balls, ball) lovr.physics.newDistanceJoint(frame, ball, vec3(x, 2, -2 + 0.25), vec3(x, 1, -2)) lovr.physics.newDistanceJoint(frame, ball, vec3(x, 2, -2 - 0.25), vec3(x, 1, -2)) end -- displace the last ball to set the Newton's cradle in motion local lastBall = balls[#balls] lastBall:applyLinearImpulse(.6, 0, 0) lovr.graphics.setBackgroundColor(0.1, 0.1, 0.1) end function lovr.draw(pass) pass:setColor(0, 0, 0) pass:box(framePose) pass:setColor(1, 1, 1) for i, ball in ipairs(balls) do local position = vec3(ball:getPosition()) pass:sphere(position, radius) end end function lovr.update(dt) world:update(dt) end