-- Lesson 26: Characters That Move Themselves. The finished script, exactly as the lesson ends. local ROOM = 20 local WALL_HEIGHT = 2 local AGENT_RADIUS = 0.4 local AGENT_HEIGHT = 1.8 local WALK_SPEED = 2.2 sceneEnable(true) sceneSetBackground(28, 32, 42, 255) sceneSetAmbient(80, 86, 100) local floorLook = materialNew() materialSetColor(floorLook, 96, 102, 110) materialSetRoughness(floorLook, 0.9) local wallLook = materialNew() materialSetColor(wallLook, 150, 128, 104) materialSetRoughness(wallLook, 0.8) local walkerLook = materialNew() materialSetColor(walkerLook, 225, 85, 60) materialSetEmissive(walkerLook, 70, 18, 10) local wandererLook = materialNew() materialSetColor(wandererLook, 80, 150, 225) materialSetEmissive(wandererLook, 10, 30, 70) local ground = nodeNew() nodeSetMesh(ground, meshPlane(ROOM, ROOM), floorLook) local wallMesh = meshBox(1, WALL_HEIGHT, 1) local walls = {} function addWall(x, z, width, depth) local wall = nodeNew() nodeSetMesh(wall, wallMesh, wallLook) nodeSetScale(wall, width, 1, depth) nodeSetPosition(wall, x, WALL_HEIGHT / 2, z) walls[#walls + 1] = wall end function makeWalker(x, z, look) local walker = nodeNew() nodeSetPosition(walker, x, 0, z) local body = nodeNew() nodeSetParent(body, walker) nodeSetMesh(body, meshBox(0.7, AGENT_HEIGHT, 0.7), look) nodeSetPosition(body, 0, AGENT_HEIGHT / 2, 0) local nose = nodeNew() nodeSetParent(nose, body) nodeSetMesh(nose, meshSphere(0.22, 16), look) nodeSetPosition(nose, 0, 0.4, -0.45) return walker end addWall(-4, 0, 12, 0.6) addWall(7.5, 0, 5, 0.6) addWall(-3, -6, 0.6, 8) local nav = navNew(AGENT_RADIUS, AGENT_HEIGHT, 45, 0.3) navAddNode(nav, ground) for _, wall in ipairs(walls) do navAddNode(nav, wall) end navBuild(nav) local walker = makeWalker(-7, 7, walkerLook) local walkerAgent = navAgentNew(nav, walker, AGENT_RADIUS, AGENT_HEIGHT, WALK_SPEED) local wanderer = makeWalker(6, -6, wandererLook) local wandererAgent = navAgentNew(nav, wanderer, AGENT_RADIUS, AGENT_HEIGHT, 1.4) local posts = { { 7, 0, 7 }, { 7, 0, -7 }, { -7, 0, -7 }, { -7, 0, 7 }, { 30, 0, 30 } } local post = 0 local message = "Button 1 sends the red one somewhere." local sun = lightNew(LIGHT_DIRECTIONAL) nodeSetPosition(sun, 8, 14, 6) nodeLookAt(sun, 0, 0, 0) lightSetIntensity(sun, 3.5) lightSetShadow(sun, true) local camera = nodeNew() nodeSetPosition(camera, 0, 17, 19) nodeLookAt(camera, 0, 0, 0) cameraSet(camera) cameraSetPerspective(55, 0.1, 120) navAgentMoveTo(wandererAgent, navRandomPoint(nav)) function onInputPressed(what) if what ~= SWITCH_BUTTON1 then return end post = post % #posts + 1 local target = posts[post] if navAgentMoveTo(walkerAgent, target[1], target[2], target[3]) then message = string.format("Walking to post %d.", post) else message = string.format("No path to post %d. Staying put.", post) end end function onNavArrived(agent) if agent == wandererAgent then navAgentMoveTo(agent, navRandomPoint(nav)) else message = string.format("Arrived at post %d.", post) end end function onOverlayUpdate() overlayClear() navDraw(nav, 0, 170, 190) local target = posts[post] if target ~= nil then local x, y, z = nodeGetWorldPosition(walker) local route = navPath(nav, x, y, z, target[1], target[2], target[3]) if route ~= nil then for i = 2, #route do local a = route[i - 1] local b = route[i] lineDraw(a[1], a[2] + 0.1, a[3], b[1], b[2] + 0.1, b[3], 255, 215, 60) end end end local vx, vy, vz = navAgentGetVelocity(walkerAgent) local speed = math.sqrt(vx * vx + vz * vz) overlayPrint(2, 2, message) overlayPrint(2, 4, string.format("Speed %.2f", speed)) return OVERLAY_UPDATED end