I recently got into Roblox development after several years of being on the platform and I have been doing a lot of reading up on scripting certain things that I want to script. After all that reading, I’m working on making an AI pathfinding system for an AI that can most effectively chase after someone and catch them, basically the “smart zombie” idea that a lot of people go for. Despite plenty of implementations, such as a combination of TheDevKing’s tutorials that recalculated the path after each waypoint and Y3llow Mustang’s “advanced zombie” scripts wholesale, I haven’t found the success I’m looking for with this system. Here’s a quick run down of what I want:
- I want something that allows the AI to chase after someone as smoothly/naturally as if the script were just looping “:MoveTo()” the person, but with the ability to jump and navigate obstacles at roughly the same speed the average player would.
- I want something that moves the AI to where the player is instead of where they were. I’ve already tried running the pathfinding service through a loop (create path then compute path then move through all waypoints before creating next path etc.) and it is effortless to evade the AI’s movements because it has to complete the path it previously computed before it moves to the next one. It will never actually hit you.
- I want something that’s efficient as possible and puts minimal strain on the game engine because this is going to be replicated a lot, with possibly dozens of active instances running at the same time.
With all that said, the code below is my latest implementation. I’m having multiple problems right now with it (apologies because I don’t know how to film it and pictures wouldn’t do it justice) but the main two are:
- When the target is moving, the AI stops in its tracks and doesn’t begin moving again until the target stops moving.
- Very often, if the target moves to a location that the AI needs to jump in order to reach, the AI will continuously jump and, instead of navigating a practical path, will move directly towards the target in a straight line, even if it’s impossible for the AI to reach the target from that trajectory.
I included comments, but the tl;dr of the pathfinding thought process is for the bot to compute the path, then follow all the waypoints, but if the target changes, the bot will stop following the waypoints and will instead recompute the path and follow those waypoints instead. I hope this all makes sense. The code is below:
-- SERVICES local PFS = game:GetService("PathfindingService") -- PATH OBJECT local path = PFS:CreatePath() -- VARIABLES local test = script.Parent local hum = test:WaitForChild("Humanoid") local HRP = test:WaitForChild("HumanoidRootPart") -- FUNCTIONS local function findTarget() local aggroDistance = 100 local target for i, v in pairs(game.Workspace:GetChildren()) do local humanoid = v:FindFirstChild("Humanoid") local humanoidrootpart = v:FindFirstChild("HumanoidRootPart") if humanoid and humanoidrootpart and v ~= script.Parent then -- check distance if (HRP.Position - humanoidrootpart.Position).Magnitude < aggroDistance then aggroDistance = (HRP.Position - humanoidrootpart.Position).Magnitude target = humanoidrootpart end end end return target end -- (necessary for smoothness) test.PrimaryPart:SetNetworkOwner(nil) -- MAIN LOOP while true do local target = findTarget() -- if a target exists, then run this loop until their health is zero if target then repeat -- get the target's current position down in case it changes, then compute the path local currentPosition = target.Position local success, errorMsg = pcall(function() path:ComputeAsync(HRP.Position, currentPosition) end) -- if the path was computed and it's possible, then get the waypoints and move through each one if success and path.Status == Enum.PathStatus.Success then local waypoints = path:GetWaypoints() for _, waypoint in pairs(waypoints) do -- if the waypoint requires the bot to jump and the bot isn't already jumping, then jump if waypoint.Action == Enum.PathWaypointAction.Jump and hum.Jump == false then hum.Jump = true wait() hum.Jump = false end hum:MoveTo(waypoint.Position) -- if the target's current position does not match that of the position we logged, then stop following the path, break out of the loop and start over if target.Position ~= currentPosition then break end end -- otherwise if there's no possible path, then tell me there's no possible path elseif path.Status == Enum.PathStatus.NoPath then print("No possible path") -- otherwise if the path couldn't be computed, then tell me it couldn't be computed else warn("Failed to compute path: ", errorMsg) end until target.Parent.Humanoid.Health == 0 else hum:MoveTo(HRP.Position + Vector3.new(math.random(-50, 50), 0, math.random(-50, 50))) hum.MoveToFinished:Wait(2) end end
Please let me know if you have any questions. I can possibly get footage for next time if someone is willing to recommend methods for recording in Studio (I tried the in-game video recorder and got mixed results).