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

0

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

0

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. 

0

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.

0

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

0

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

0

@imKirda I did that on line 10

0

@imKirda I did that on line 10