-- 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