I have made a Zombie Artificial Intelligence script that chases the nearest player to the zombie using a mixture of pathfinding, raycasting and the MoveTo() function. The overall gist of the code works, however the zombie keeps pausing after each waypoint and this is just not practical.
Heres the code I wrote:
local pathfindingservice = game:GetService("PathfindingService")
local db = false
local torso = nil
local torsochar = nil
local zombie = script.Parent:WaitForChild("Zombie")
local me = script.Parent:WaitForChild("Head")
script.Parent.PrimaryPart:SetNetworkOwner(nil)
local target = nil
local aim
local path
local a = 1
local movetotimeout = 0
local movementdone = false
local chooserdb = false
function FindNearest(position)
local lowest = math.huge -- infinity
local NearestPlayer = nil
for i,v in pairs(game.Players:GetPlayers())do
if v and v.Character then
local distance = v:DistanceFromCharacter(position)
if distance < lowest then
lowest = distance
NearestPlayer = v
end
end
end
return NearestPlayer
end
function targetposition()
torso = FindNearest(me.Position)
if torso ~= nil then
torsochar = game.Workspace:FindFirstChild(torso.Name)
-- print("torsochar"..tostring(torsochar.HumanoidRootPart.Position))
target = torsochar.HumanoidRootPart.Position
end
return target
end
-- if target is within a radius and raycasting shows that there is nothing in the way,
-- use MoveTo (on loop) but if ray casting shows something in the way then use pathfinder.
function movementchooser()
if chooserdb == false then
chooserdb = true
local barrierfinder = Ray.new(me.Position, (targetposition() - me.Position).Unit * 500)
local hit, position = game.Workspace:FindPartOnRayWithIgnoreList(barrierfinder, script.Parent:GetChildren())
if hit == torsochar:WaitForChild("RayHitbox") then
print("MoveTo selected")
chooserdb = false
return "MoveTo"
else
print("PathFind Selected")
chooserdb = false
return "Pathfind"
end
end
end
function Pathfinding()
aim = targetposition()
path = pathfindingservice:CreatePath()
path:ComputeAsync(me.Position, aim)
local waypoints = path:GetWaypoints()
for i, waypoint in pairs(waypoints)do
if waypoint.Action == Enum.PathWaypointAction.Jump then
zombie:ChangeState(Enum.HumanoidStateType.Jumping)
end
zombie:MoveTo(waypoint.Position)
zombie.MoveToFinished:Wait()
end
return "done"
end
function MoveTo()
print("MoveTo Activated")
movementdone = false
movetotimeout = 0
zombie:MoveTo(targetposition())
while movetotimeout < 1 do
wait(0.1)
movetotimeout = movetotimeout + 0.1
print("MoveTo Check", movetotimeout)
end
if movetotimeout >= 1 then
print("timed out")
movementchooser()
if movementchooser() == "Pathfind" then
Pathfinding()
repeat
wait()
until Pathfinding() == "done"
end
else
if zombie.MoveToFinished == true then
print("MoveTo Finished")
movementdone = true
return "Done"
end
end
movementdone = true
return "Done"
end
wait(15)
while true do
MoveTo()
repeat
wait()
until movementdone == true
wait()
end
I would prefer to only use pathfinding service however I have found that it won't change target position when the nearest player moves.
Any suggestions will be very much appreciated, Thanks.

7 comments
I think that's a problem that can't be fixed with MoveToFinished. I usually make a lerp from a point to another and then on a local script the zombie is "rendered" and tweened and animated everytime the server zombie position changes, so for the player the enemy moves smoothly. The time to wait I use is (Distance/WalkSpeed/lerpcycles). For example, if the distance from pointa to pointb is 64 studs
Maybe you could add a part on the next waypoint and bind a touched function to check when the bot touchs it, so it know it got close enough. Making that part a bit big could prevent him from stopping, but it can also result in bad pathing on narrow places.
I'm not very good at this kind of thing, but i think what you should do is when the player moves or its position is changed, calculate a new path, and move along that one. As far as i can tell, there isn't a need to use RayCasting. Although i've never used raycasting, so correct me if i'm wrong.
Make sure to set NetworkOwner of the AI's humanoid root part to nil because if it is somehow not the client will try to handle the AI but you need server to do it
Make sure to set NetworkOwner of the AI's humanoid root part to nil because if it is somehow not the client will try to handle the AI but you need server to do it
@imKirda I did that on line 10
@imKirda I did that on line 10