-- Vehicles: a car built from primitives (a box chassis with a dynamic body, four wheel nodes) on a -- track with a ramp, crates and a wall, driven by a scripted route: pull away, jump the ramp, brake, -- turn, and reverse into the crates. The camera chases. local font = fontLoad("Singe/FreeSansBold.ttf", 28) local frames = 0 local turned = false local reversed = false fontSelect(font) discPlay() sceneEnable(true) sceneSetBackground(0, 0, 0, 0) sceneSetAmbient(70, 70, 80) local asphalt = materialNew() materialSetColor(asphalt, 70, 70, 75) materialSetRoughness(asphalt, 0.95) local paint = materialNew() materialSetColor(paint, 200, 40, 40) materialSetMetallic(paint, 0.5) materialSetRoughness(paint, 0.3) local rubber = materialNew() materialSetColor(rubber, 30, 30, 30) local wood = materialNew() materialSetColor(wood, 150, 100, 50) local function block(x, y, z, w, h, d, material, rz) local node = nodeNew() nodeSetMesh(node, meshBox(w, h, d), material) nodeSetPosition(node, x, y, z) if rz then nodeSetRotation(node, rz, 0, 0) end bodyNew(node, BODY_STATIC, SHAPE_BOX, w, h, d) return node end -- The track runs along -Z (the car's nose). A long floor, a ramp, a landing, crates and a wall. block(0, -0.1, -60, 40, 0.2, 200, asphalt) block(0, 0.45, -18, 6, 0.2, 5, asphalt, 12) block(0, 0.9, -23.4, 6, 0.2, 6, asphalt) for i = 0, 5 do local crate = nodeNew() nodeSetMesh(crate, meshBox(0.6, 0.6, 0.6), wood) nodeSetPosition(crate, -3 + (i % 3) * 0.65, 0.3 + math.floor(i / 3) * 0.62, -36) bodyNew(crate, BODY_DYNAMIC, SHAPE_BOX, 0.6, 0.6, 0.6) bodySetMass(crate, 4) end block(8, 1, -30, 0.3, 2, 12, asphalt) -- The car: chassis 1.8 x 0.5 x 4.0, wheels as children whose X axis is the axle. local car = nodeNew() nodeSetMesh(car, meshBox(1.8, 0.5, 4.0), paint) nodeSetPosition(car, 0, 0.9, 0) bodyNew(car, BODY_DYNAMIC, SHAPE_BOX, 1.8, 0.5, 4.0) bodySetMass(car, 1500) vehicleNew(car, VEHICLE_CAR) local wheels = {} for i, p in ipairs({ { -0.95, -0.2, -1.4 }, { 0.95, -0.2, -1.4 }, { -0.95, -0.2, 1.4 }, { 0.95, -0.2, 1.4 } }) do local wheel = nodeNew() nodeSetParent(wheel, car) nodeSetPosition(wheel, p[1], p[2], p[3]) local tyre = nodeNew() nodeSetParent(tyre, wheel) nodeSetMesh(tyre, meshCylinder(0.35, 0.25, 16), rubber) nodeSetRotation(tyre, 0, 0, 90) wheels[i] = vehicleAddWheel(car, wheel, 0.35, 0.25, 0.4) end vehicleSetEngine(car, 600, 6500, 1000) vehicleSetAntiRoll(car, 800) local sun = lightNew(LIGHT_DIRECTIONAL) nodeSetPosition(sun, -5, 12, 6) nodeLookAt(sun, 0, 0, -15) lightSetIntensity(sun, 1.5) lightSetShadow(sun, true) local camera = nodeNew() cameraSet(camera) cameraSetPerspective(60, 0.1, 200) function onOverlayUpdate() local cx, cy, cz = nodeGetPosition(car) local t = frames / 60 frames = frames + 1 -- Route by distance, since physics runs on the clock: floor it over the ramp, then brake and -- steer, then reverse toward the crates, then stop. if cz > -30 and not turned then vehicleDrive(car, 1, 0, 0, 0) elseif not turned then vehicleDrive(car, 0, 0.7, 0.6, 0) if vehicleGetSpeed(car) < 2 then turned = true end elseif not reversed then vehicleDrive(car, -1, 0, 0, 0) if vehicleGetSpeed(car) < -6 then reversed = true end else vehicleDrive(car, 0, 0, 1, 1) end nodeSetPosition(camera, cx + 3, cy + 3, cz + 9) nodeLookAt(camera, cx, cy, cz - 3) overlayClear() fontPrint(20, 20, string.format("Car, frame %d speed %.1f m/s gear %d rpm %.0f", frames, vehicleGetSpeed(car), vehicleGetGear(car), vehicleGetRpm(car))) if frames % 30 == 0 then debugPrint(string.format("frame %d car %.1f %.2f %.1f speed %.1f gear %d rpm %.0f ground %s %s %s %s slip %.2f", frames, cx, cy, cz, vehicleGetSpeed(car), vehicleGetGear(car), vehicleGetRpm(car), tostring(vehicleIsWheelOnGround(car, 0)), tostring(vehicleIsWheelOnGround(car, 1)), tostring(vehicleIsWheelOnGround(car, 2)), tostring(vehicleIsWheelOnGround(car, 3)), vehicleGetWheelSlip(car, 2))) end if frames == 60 or frames == 120 or frames == 170 then singeScreenshot() end if frames == 180 then singeQuit() end end