Help with zombie ai

I’m creating a zombie game with a friend and I was tasked with scripting the zombie AI, it works pretty good however it twitches while walking and I can’t figure out why. If somebody knows how to fix this please let me know how.

Thanks in advance to all who reply :slightly_smiling_face:

Here’s my code:

local PathFindingService = game:GetService("PathfindingService")
local Players = game:GetService("Players")

local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local root = character:WaitForChild("HumanoidRootPart")

root:SetNetworkOwner(nil)

local followRange = 70 -- In studs
local lastWander = tick()

local function createPath(a, b)
	local size = character:GetExtentsSize()
	
	local params = {
		["AgentRadius"] = (size.X + size.Z) / 4,
		["AgentHeight"] = size.Y,
		["AgentCanJump"] = true,
	}
	
	local path = PathFindingService:CreatePath(params)
	
	local created, result = pcall(function()
		return path:ComputeAsync(a, b)
	end)
	
	if created then
		return path
	else
		warn(result)		
	end
end

local function walkTo(path)
	if not path then
		return
	end
	
	local waypoints = path:GetWaypoints()
	
	for _, waypoint in pairs(waypoints) do
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			humanoid.Jump = true
		end
		
		humanoid:MoveTo(waypoint.Position)
		
		local timeOut = humanoid.MoveToFinished:Wait()
		if not timeOut then
			humanoid.Jump = true
			walkTo(path)
		end
	end
end

function getTarget()
	local least = math.huge
	local instance = nil
	
	for _, player in pairs(Players:GetPlayers()) do
		local char = player.Character
		if char then
			local mag = (char.HumanoidRootPart.Position - root.Position).Magnitude
			if mag < least and mag <= followRange then
				least = mag
				instance = char.HumanoidRootPart.Position
			end
		end
	end
	
	return instance
end

while wait(0.5) do
	local target = getTarget()
	if target then
		local path = createPath(root.Position, target)
		coroutine.wrap(walkTo)(path)
	else
		if tick() - lastWander >= 5 then
			local randX = math.random(-10, 10)
			local randZ = math.random(-10, 10)
			humanoid:MoveTo(Vector3.new(randX, 0, randZ))
			humanoid.MoveToFinished:Wait()
			lastWander = tick()
		end
	end
end

I read in another post that setting the NetworkOwnership of all of the BaseParts will decrease lag. Try setting the NetworkOwnership to all of the BaseParts in the model to nil, instead of just the RootPart.

1 Like

Didn’t really make much of a difference, the zombie is still twitching like it’s second guessing itself.

Could you send a video? Perhaps there are multiple targets that it’s trying to lock onto?

I’ve only tested it with 1 player so far, however I think it may have to do with the fact I’m calling humanoid:MoveTo while it’s already moving.

1 Like

That may be the reason. It looks like you’re creating a new path to the target every 0.5 seconds. That might create some twitching effects as the zombie has to create a new path to its target 2 times every second.

I think its cause you are confusing the agent by giving it multiple instructions without ever stopping them in the coroutine call. Perhaps maybe just have it in the main script which it is called and just have like a variable which is being changed

First. You should check Check if you can raycast to the target and if you can you do not use pathfinding at all.
Raycasting | Documentation - Roblox Creator Hub You should ignoreble instances are parts inside models which have a humanoids parts named Handle and parts which have .CanCollidable = false also when you detect that you hit a part but you ignore it then you reraycast and add the ignored part to the ignore list.

Also I suggest you handle when the path is blocked via the path.Blocked event. See Character Pathfinding | Documentation - Roblox Creator Hub for more info.
Also replace

while wait(0.5) do

with

while true do
wait(1.5)

Also use

time()

instead of

tick()