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).