Pathfinding NPC Bugging

Hello everyone! I’ve been trying to make an NPC that chases a player using PathfindingService, however over some time, the NPC bugs more and more. By bugging, I mean the NPC stops for a split second at each waypoint before continuing, instead of smoothly passing by each waypoint.

Here is a video of what I mean:

I am wondering if there was a way to stop this “buggy” movement so it can move smoothly all the time.

Here is the code for the NPC:

local PathfindingService = game:GetService("PathfindingService")

local ClosestDistance = 0
local HumanoidRootPartPosition
local Target

function GetNearest()
	for i,v in pairs(workspace:GetChildren()) do
		if game.Players:GetPlayerFromCharacter(v) then
			if v:FindFirstChild("player") then
				if not Target then
					Target = game.Players:GetPlayerFromCharacter(v)
					ClosestDistance = (v.HumanoidRootPart.Position - script.Parent.HumanoidRootPart.Position).Magnitude
					HumanoidRootPartPosition = v.HumanoidRootPart.Position
				else
					local Magnitude = (v.HumanoidRootPart.Position - script.Parent.HumanoidRootPart.Position).Magnitude
					if Magnitude < ClosestDistance and game.Players:GetPlayerFromCharacter(v) ~= Target then
						Target = game.Players:GetPlayerFromCharacter(v)
						ClosestDistance = (v.HumanoidRootPart.Position - script.Parent.HumanoidRootPart.Position).Magnitude
						HumanoidRootPartPosition = v.HumanoidRootPart.Position
					end
				end
				if HumanoidRootPartPosition then
					HumanoidRootPartPosition = Vector3.new(math.floor(HumanoidRootPartPosition.X),math.floor(HumanoidRootPartPosition.Y),math.floor(HumanoidRootPartPosition.Z))
				end
			end
		end
	end
end

while wait() do
	if Target then
		local Path = PathfindingService:CreatePath()
		Path:ComputeAsync(script.Parent.HumanoidRootPart.Position,Target.Character.HumanoidRootPart.Position)
		local Waypoints = Path:GetWaypoints()
		if Path.Status == Enum.PathStatus.Success then
			local OldPosition = HumanoidRootPartPosition
			for i,Point in pairs(Waypoints) do
				script.Parent.Humanoid:MoveTo(Point.Position)
				if Point.Action == Enum.PathWaypointAction.Jump then
					script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
				end
				local timeout = script.Parent.Humanoid.MoveToFinished:Wait(0.5)
				if not timeout then
					script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
					break
				end
				if i % 3 == 0 then
					HumanoidRootPartPosition = Target.Character.HumanoidRootPart.Position
					HumanoidRootPartPosition = Vector3.new(math.floor(HumanoidRootPartPosition.X),math.floor(HumanoidRootPartPosition.Y),math.floor(HumanoidRootPartPosition.Z))
					if HumanoidRootPartPosition ~= OldPosition then
						break
					end
					OldPosition = HumanoidRootPartPosition
				end
			end
		else
			script.Parent.Humanoid:MoveTo(Target.Character.HumanoidRootPart.Position)
		end
	end
	GetNearest()
end

Thank you for reading! Hope you can help. :slight_smile:

2 Likes

It can be because of Humanoid.MoveToFinished that’s causing it to stutter, maybe make a loop that repeats wait() until it finally reached the waypoint by checking the magnitude

I tried to add

repeat wait() until math.floor((script.Parent.HumanoidRootPart.Position-Point.Position).Magnitude) <= 3

to the code, however the NPC then starts to behave like this:

Maybe try doing 1 studs instead, see if it stutters

If I set it to 1, it stands completely still and doesn’t move, since the height of the HumanoidRootPart is higher than 1 stud off of the ground, and waypoints generate on the ground.

Does 5 studs makes it move smoother?

Your stutter problem here is because you are waiting for the NPC to reach every waypoint and then after it’s reached the script is yielded and then moves the NPC again, I commented out the code that you do not need.

This should work fine. If you encounter any problems or have any questions please let me know.

-- I suggest you create the path once, and just keep recomputing it, you do not need to create a new path every time
if Path.Status == Enum.PathStatus.Success then
	--local OldPosition = HumanoidRootPartPosition
	for i,Point in pairs(Waypoints) do
		if Point.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid.Jump = true
		end
		script.Parent.Humanoid:MoveTo(Point.Position)
		--local timeout = script.Parent.Humanoid.MoveToFinished:Wait(0.5) -- this is causing the stutter
		--if not timeout then
		--	script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		--	break
		--end
		--if i % 3 == 0 then 
		--	HumanoidRootPartPosition = Target.Character.HumanoidRootPart.Position
		--	HumanoidRootPartPosition = Vector3.new(math.floor(HumanoidRootPartPosition.X),math.floor(HumanoidRootPartPosition.Y),math.floor(HumanoidRootPartPosition.Z))
		--	if HumanoidRootPartPosition ~= OldPosition then
		--		break
		--	end
		--	OldPosition = HumanoidRootPartPosition
		--end
	end
else
	--[[ Here you're supposed to recalculate the path not just move the npc towards the target
	     I suggest you change this into its own function so you can recall the computing function]]--
	script.Parent.Humanoid:MoveTo(Target.Character.HumanoidRootPart.Position)
end
2 Likes

Thank you so much! This did make my code a lot shorter, and I had figured out that you can just get the second waypoint in the Waypoints table and keep recalculating it so that it runs smoothly, and continuously updates the path to the nearest player.