-- Physics stage 3: a trigger volume in the crates' path, collision callbacks, and a raycast -- through the middle of the overlay that flicks whatever it hits. local font = fontLoad("Singe/FreeSansBold.ttf", 28) local frames = 0 local entered, left, collisions, lastSpeed, picks = 0, 0, 0, 0, 0 fontSelect(font) discPlay() sceneEnable(true) sceneSetBackground(18, 18, 30, 255) sceneSetAmbient(70, 70, 85) local floorLook = materialNew() materialSetColor(floorLook, 120, 110, 100) materialSetRoughness(floorLook, 0.9) local crateLook = materialNew() materialSetColor(crateLook, 200, 140, 60) local ballLook = materialNew() materialSetColor(ballLook, 60, 140, 230) local paddleLook = materialNew() materialSetColor(paddleLook, 230, 60, 40) local zoneLook = materialNew() materialSetColor(zoneLook, 80, 255, 120, 70) materialSetBlend(zoneLook, true) materialSetUnlit(zoneLook, true) local markLook = materialNew() materialSetColor(markLook, 255, 255, 80) materialSetEmissive(markLook, 120, 120, 0) local floor = nodeNew() nodeSetMesh(floor, meshBox(14, 0.2, 14), floorLook) nodeSetPosition(floor, 0, -1.9, 0) bodyNew(floor, BODY_STATIC, SHAPE_BOX, 14, 0.2, 14) local crates = {} for i = 1, 4 do local crate = nodeNew() nodeSetMesh(crate, meshBox(0.6, 0.6, 0.6), crateLook) nodeSetPosition(crate, -1.0, -1.5 + (i - 1) * 0.62, 0) bodyNew(crate, BODY_DYNAMIC, SHAPE_BOX, 0.6, 0.6, 0.6) crates[i] = crate end local ball = nodeNew() nodeSetMesh(ball, meshSphere(0.35, 32), ballLook) nodeSetPosition(ball, 0.2, 1.5, 0.3) bodyNew(ball, BODY_DYNAMIC, SHAPE_SPHERE, 0.35) bodySetBounce(ball, 0.5) -- The trigger: a translucent green volume to the right of the stack. local zone = nodeNew() nodeSetMesh(zone, meshBox(1.6, 1.6, 2.4), zoneLook) nodeSetPosition(zone, 1.8, -1.0, 0) bodyNew(zone, BODY_STATIC, SHAPE_BOX, 1.6, 1.6, 2.4) bodySetTrigger(zone, true) local paddle = nodeNew() nodeSetMesh(paddle, meshBox(0.3, 1.2, 1.6), paddleLook) nodeSetPosition(paddle, -4.0, -1.2, 0) bodyNew(paddle, BODY_KINEMATIC, SHAPE_BOX, 0.3, 1.2, 1.6) -- A marker placed where the pick ray hits. local mark = nodeNew() nodeSetMesh(mark, meshSphere(0.08, 12), markLook) nodeSetVisible(mark, false) local sun = lightNew(LIGHT_DIRECTIONAL) nodeSetPosition(sun, -3, 6, 4) nodeLookAt(sun, 0, 0, 0) lightSetIntensity(sun, 1.2) lightSetShadow(sun, true) local camera = nodeNew() nodeSetPosition(camera, 1.0, 1.6, 6.5) nodeLookAt(camera, 0.2, -0.6, 0) cameraSet(camera) function onCollision(a, b, x, y, z, speed) collisions = collisions + 1 lastSpeed = speed end function onTrigger(trigger, other, isEntering) if isEntering then entered = entered + 1 else left = left + 1 end debugPrint(string.format("trigger %d %s node %d", trigger, isEntering and "entered by" or "left by", other)) end function onOverlayUpdate() frames = frames + 1 overlayClear() fontPrint(20, 20, string.format("Physics %d hits %d (%.1f) in %d out %d picks %d", frames, collisions, lastSpeed, entered, left, picks)) if frames >= 60 and frames < 150 then nodeMove(paddle, 0.06, 0, 0) end if frames % 30 == 0 and frames <= 240 then -- A ray from the camera through the overlay's centre; flick whatever it hits upward. local w, h = sceneGetSize() local ox, oy, oz = sceneUnproject(w / 2, h / 2, 0) local fx, fy, fz = sceneUnproject(w / 2, h / 2, 10) local hit, hx, hy, hz = physicsRaycast(ox, oy, oz, fx - ox, fy - oy, fz - oz) if hit then picks = picks + 1 nodeSetVisible(mark, true) nodeSetPosition(mark, hx, hy, hz) if hit ~= floor and hit ~= zone and hit ~= paddle then bodyApplyImpulse(hit, 0, 3, 0, hx, hy, hz) end debugPrint(string.format("pick node %d at %.2f %.2f %.2f", hit, hx, hy, hz)) end end if frames == 50 or frames == 130 or frames == 220 then singeScreenshot() end if frames == 250 then debugPrint(string.format("RESULT collisions=%d entered=%d left=%d picks=%d", collisions, entered, left, picks)) singeQuit() end end