singe/docs/learn/24-physics.singe
2026-09-22 21:57:42 -05:00

162 lines
3.8 KiB
Text

-- Lesson 24: Physics. The finished script, exactly as the lesson ends.
local crates = {}
local balls = {}
local aimX = 0
local aimLeft = false
local aimRight = false
local knocks = 0
local showShapes = false
sceneEnable(true)
sceneSetBackground(16, 18, 26)
sceneSetAmbient(40, 42, 50)
local stone = materialNew()
materialSetColor(stone, 110, 110, 120)
materialSetRoughness(stone, 0.9)
local wood = materialNew()
materialSetColor(wood, 190, 140, 80)
materialSetRoughness(wood, 0.8)
local steel = materialNew()
materialSetColor(steel, 200, 205, 215)
materialSetMetallic(steel, 1)
materialSetRoughness(steel, 0.25)
local floor = nodeNew()
nodeSetMesh(floor, meshBox(16, 0.4, 16), stone)
nodeSetPosition(floor, 0, -0.2, 0)
bodyNew(floor, BODY_STATIC, SHAPE_BOX, 16, 0.4, 16)
local wallMesh = meshBox(16, 1, 0.4)
for _, side in ipairs({ { 0, -8, 0 }, { 0, 8, 0 }, { -8, 0, 90 }, { 8, 0, 90 } }) do
local wall = nodeNew()
nodeSetMesh(wall, wallMesh, stone)
nodeSetPosition(wall, side[1], 0.5, side[2])
nodeSetRotation(wall, 0, side[3], 0)
bodyNew(wall, BODY_STATIC, SHAPE_BOX, 16, 1, 0.4)
end
local crateMesh = meshBox(0.8, 0.8, 0.8)
local function crateAt(x, y)
local crate = nodeNew()
nodeSetMesh(crate, crateMesh, wood)
nodeSetPosition(crate, x, y, -2)
bodyNew(crate, BODY_DYNAMIC, SHAPE_BOX, 0.8, 0.8, 0.8)
bodySetMass(crate, 4)
crates[#crates + 1] = crate
end
local function fire()
local ball = nodeNew()
nodeSetMesh(ball, meshSphere(0.35, 24), steel)
nodeSetPosition(ball, aimX, 1.2, 6)
bodyNew(ball, BODY_DYNAMIC, SHAPE_SPHERE, 0.35)
bodySetMass(ball, 3)
bodySetBounce(ball, 0.3)
bodySetVelocity(ball, 0, 2, -14)
balls[#balls + 1] = ball
if #balls > 5 then
nodeDelete(balls[1])
table.remove(balls, 1)
end
end
crateAt(-0.9, 0.4)
crateAt(0, 0.4)
crateAt(0.9, 0.4)
crateAt(-0.45, 1.25)
crateAt(0.45, 1.25)
crateAt(0, 2.1)
local marker = nodeNew()
nodeSetMesh(marker, meshBox(0.7, 0.1, 0.7), steel)
nodeSetPosition(marker, 0, 0.05, 6)
local sun = lightNew(LIGHT_DIRECTIONAL)
nodeSetPosition(sun, -5, 9, 6)
nodeLookAt(sun, 0, 0, -2)
lightSetIntensity(sun, 1.7)
lightSetShadow(sun, true)
local camera = nodeNew()
nodeSetPosition(camera, 0, 4, 11)
nodeLookAt(camera, 0, 1, -2)
cameraSet(camera)
cameraSetPerspective(55, 0.1, 200)
function onCollision(nodeA, nodeB, x, y, z, speed)
if speed > 4 then
knocks = knocks + 1
end
end
function onInputPressed(what)
if what == SWITCH_BUTTON1 then
fire()
elseif what == SWITCH_BUTTON3 then
showShapes = not showShapes
if showShapes then
physicsSetDebug(DEBUG_SHAPES + DEBUG_CONTACTS)
else
physicsSetDebug(DEBUG_NONE)
end
elseif what == SWITCH_LEFT then
aimLeft = true
elseif what == SWITCH_RIGHT then
aimRight = true
end
end
function onInputReleased(what)
if what == SWITCH_LEFT then
aimLeft = false
elseif what == SWITCH_RIGHT then
aimRight = false
end
end
function onOverlayUpdate()
local over = 0
local settled = true
if aimLeft then
aimX = math.max(aimX - 0.08, -5)
end
if aimRight then
aimX = math.min(aimX + 0.08, 5)
end
nodeSetPosition(marker, aimX, 0.05, 6)
for _, crate in ipairs(crates) do
local pitch, _, roll = nodeGetRotation(crate)
if math.abs(pitch) > 30 or math.abs(roll) > 30 then
over = over + 1
end
if not bodyIsResting(crate) then
settled = false
end
end
overlayClear()
overlayPrint(2, 2, "Arrows aim. Space fires. Shift shows the shapes.")
overlayPrint(2, 4, "knocked over " .. over .. " of " .. #crates .. " hard hits " .. knocks)
if settled then
overlayPrint(2, 6, "everything has settled")
end
return OVERLAY_UPDATED
end