Hello everyone, I spend my main time in BeamNG.drive with experimenting with setting up and spectating AI races. I'd also like those cars to recover, when they're stuck on the track. - Is there a way to track the progress of the AI vehicles on the calculated path? - And is there a way to recover the vehicle based on the path it needs to follow?
Hello there! I've taken a look at the flowgraph editor. Flowgraph is great to create missions and integrate them into the BeamNG maps. But I don't want to create a classical "mission" to play for other players; my goal is to have a function, UI app or something similar to quickly set up AI races to spectate. So I can spontaneously set up a route for all vehicles spawned in the scene (including the player vehicle) to race along. And if a vehicle gets stuck, it should recover and continue along its route instead of starting all over from the first node of the list. In other words: When I reset a broken vehicle using Recover, Reset, Load Home, recovery.recoverInPlace() or ai.setRecoverOnCrash(true) the path progress, as well as the number of laps already passed, gets lost. So I have to manually bring the car back into the race - starting it all over again instead of continuing the route it's been on. And if 10 or more vehicles are involved within the race, I spend more time resetting vehicles and estimating their progress than spectating them...
Hello again, I managed to get started with a Lua script that almost does what I want. But it still has some bugs I couldn't fix until now. The following script is a Vehicle Extension script with which I can quickly start AI races from the console. There are three race start functions that assign a list of map nodes as a checkpoint route for the vehicle. When it reaches the next checkpoint of the list, it will be saved as the home point. When the car gets stuck on the path, it should reset itself on the last saved home point. After resetting three times at the same home point, the vehicle should "teleport" to the next checkpoint of the series and continue its race from there. Once a series of 5 checkpoints is passed, the vehicle should also perform a recovery in place and continue its journey along the checkpoint route. While developing the extension I try not to modify the main vehicle lua files - as recommended by the documentation. Here's the script. Copy it into lua\vehicle\extensions and load the extension from the console. local M = {} -- AI Parameters M.risk = 0.5 M.inLane = 'on' M.avoid = 'on' -- Path data local nodePos = {} local radius = {} local racePath = {} local restPath = {} local currentPos, currentRadius local cp = 0 -- Racing system local lifes = 3 -- determines how often it tries a route from one checkpoint to another. Afterwards it should teleport to the next checkpoint. local recoverTimer = 0 -- If this vehicle is slow, this timer starts. local recoverTime = 60 -- If the recoverTimer gets above this, the vehicle will reset at the last checkpoint and try again. local startTimer = 0 -- After recovering, it needs some time to reset. local racing = false -- is true if the vehicle is currently racing. local driving = false -- is true if the vehicle is currently actually driving. local function onExtensionLoaded() ai.setMode("manual") print("Vehicle "..obj:getId()..": Ready to go") end local function getNodes() -- get all nodes from map local nodes = {} nodePos = mapmgr.mapData.positions radius = mapmgr.mapData.radius for k, v in pairs(nodePos) do table.insert(nodes, k) end return nodes end local function getDR() -- get all map nodes starting with "DR" (DecalRoad) local nodes = getNodes() local roads = {} for k, v in pairs(nodes) do if string.sub(v, 1, 2) == "DR" then table.insert(roads, v) end end return roads end local function initRace() -- activates the in-race function after a race start function was called. lifes = 3 recoverTimer = 0 racing = true end local function reset() -- reset the racing functions racePath = {} racing = false driving = false end local function span() -- start a race through all map nodes. racePath = getNodes() initRace() print("Vehicle "..obj:getId()..": Starting Race") end local function spanDR() -- start a race through all DecalRoads (DR nodes) racePath = getDR() initRace() print("Vehicle "..obj:getId()..": Starting Race") end local function route(path, laps) -- start a race along a determined path. racePath = path initRace() print("Fahrzeug "..obj:getId()..": Starting Race") end local function updateGFX(dtGFX) -- called each frame local dt = dtGFX -- time between two frames local speed = vec3(obj:getSmoothRefVelocityXYZ()):length() -- copied from AI script to get the car's speed local pos = obj:getPosition() if racing then -- handle start timer if active (above 0) if startTimer > 0 then startTimer = startTimer - dt end -- get the AI driving again if it's not if not driving and startTimer <= 0 then if #restPath == 0 then for i = 1, 5 do if racePath ~= nil then table.insert(restPath, racePath[1]) table.remove(racePath, 1) end end end ai.driveUsingPath{wpTargetList = restPath, aggression = M.risk, driveInLane = M.inLane, avoidCars = M.avoid} driving = true end currentPos = nodePos[restPath[1]] currentRadius = radius[restPath[1]] -- Track route progress if pos:squaredDistance(currentPos) < currentRadius * currentRadius * 10 then cp = cp + 1 print("Vehicle "..obj:getId()..": Checkpoint "..cp.." reached.") table.remove(restPath, 1) currentPos = nodePos[restPath[1]] currentRadius = radius[restPath[1]] recovery.saveHome() end if #restPath == 0 then if driving and speed < 0.1 then if #racePath > 0 then startTimer = 1 recovery.recoverInPlace() print("Vehicle "..obj:getId()..": Stage finished. Recovering for next stage") lifes = 3 driving = false; end else print("Vehicle "..obj:getId()..": Race finished") racing = false end end -- Recover if necessary if driving and speed < 3 then recoverTimer = recoverTimer + dtGFX else recoverTimer = 0 end if recoverTimer > recoverTime then lifes = lifes - 1 recoverTimer = 0 if lifes > 0 then print("Vehicle "..obj:getId()..": Stuck on track, resetting to previous checkpoint.") startTimer = 1 driving = false recovery.loadHome() else print("Vehicle "..obj:getId()..": Section seems to be impossible. Teleporting to next checkpoint.") table.remove(restPath, 1) local newHome = { pos = restPath[1], dirFront = obj:getDirectionVector(), dirUp = vec3(0, 0, 1)} print(newHome.pos) print(newHome.dirFront) print(newHome.dirUp) recovery.saveHome(newHome) startTimer = 1 driving = false recovery.loadHome() end end -- Debug M.distance = pos:squaredDistance(currentPos) M.currentPos = currentPos M.pos = pos M.speed = speed M.timer = recoverTimer M.restPath = restPath M.lifes = lifes end end M.onExtensionLoaded = onExtensionLoaded M.span = span M.spanDR = spanDR M.route = route M.getNodes = getNodes M.getDR = getDR M.updateGFX = updateGFX M.reset = reset return M It resets the car very well, if it gets stuck on the track, but the teleportation to the next point causes the car to disable for some reason... Also, the fifth and final point of the route part list is not detected by the script, while all previous ones work fine. Do you have any idea how to fix these bugs?