NPC Pathfinding Twitching

Im trying to make an npc/pathfinding system, and whenever i test it, the npc starts twitching. Any way to fix this?

local players = game:GetService("Players")
local pathfindingService = game:GetService("PathfindingService")
local runService = game:GetService("RunService")

local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local rootPart = npc:WaitForChild("HumanoidRootPart")
local mobInfo = npc.Head:WaitForChild("MobInfo")
local soundsFolder = script.Parent.SoundsFolder

humanoid.WalkSpeed = 14

local Animation = script.Parent.AnimationsFolder.Attack
local loadedAnimation = humanoid:LoadAnimation(Animation)

local attacking = false

local function findPlayer()
	local nearestPlayer = nil
	local shortestDistance = math.huge
	
	for i, player in pairs(players:GetPlayers()) do
		if player.Character then
			local distance = (player.Character.HumanoidRootPart.Position - rootPart.Position).Magnitude
			if distance < shortestDistance and distance < 20 then
				shortestDistance = distance
				nearestPlayer = player.Character
			end
		end
	end
	
	return nearestPlayer
end



local function moveToTarget(targetPosition)
	
	local path = pathfindingService:CreatePath()
	path:ComputeAsync(rootPart.Position, targetPosition)
	
	if path.Status == Enum.PathStatus.Success then
		for i, waypoint in pairs(path:GetWaypoints()) do
			humanoid:MoveTo(waypoint.Position)
			humanoid.MoveToFinished:Wait()
		end	
	end
end

local function Attack(target)
	if attacking then return end

	attacking = true
	humanoid:MoveTo(rootPart.Position)
	loadedAnimation:Play()

	task.wait(.2)

	if target.Humanoid.Health > 0 then
		target.Humanoid:TakeDamage(10)
		soundsFolder.Growl:Play()
	end

	wait(1.25)
	attacking = false
end

runService.Heartbeat:Connect(function()
	if humanoid.Health <= 0 then return end
	
	local nearestPlayer = findPlayer()
	if nearestPlayer == nil then return end
	
	local targetRootPart = nearestPlayer:WaitForChild("HumanoidRootPart")
	local targetHumanoid = nearestPlayer:WaitForChild("Humanoid")
	if targetHumanoid == nil then return end
	
	local distance = (targetRootPart.Position - rootPart.Position).Magnitude
	if distance < 4 then
		Attack(nearestPlayer)
	else
		moveToTarget(targetRootPart.Position)
	end

end)

There are a lot of issues with your system.

The reason it’s twitching is because you’re recalculating the path every frame and calling moveToTarget while the previous call is still looping through the waypoints.

There are no easy solutions but to rewrite it.

First of all, don’t create a new path every time you call that function. Instead, call ComputeAsync only if the player is over 2 studs away from the previous target, or when Blocked is fired.

Store the waypoints outside the moveToTarget function, together with an index variable, which shows which waypoint is the current one. When calculating new waypoints, reset that index variable to 1. When the zombie gets within 1 stud of the current waypoint, increase that index by 1, and call MoveTo towards the next waypoint.

4 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.