Pathfinding Jitter Issue

Hello, I’m currently working on a project where I need to create NPC’s such as SCP-173, however I’m struggling a little. First of all, I’m aware that this is not how SCP-173 is supposed to chase people according to the SCP lore itself, however this is the way the other SCP’s are supposed to move (walk) so I’m kind of working on it for now. In order to achieve a consistent chasing system for the SCP’s, I keep updating the path every single time the NPC reaches a waypoint to prevent it from trying to move to the previous position of the player. The following is my script:

local players = game:GetService("Players")
local module = require(script.Parent)
local noob = script.Parent.Parent
local pathfinding = game:GetService("PathfindingService")

for i, part in pairs(script.Parent:GetChildren()) do
	if part:IsA("BasePart") then
		part:SetNetworkOwner(nil)
	end
end

while task.wait(0.1) do

local closestChar = module.findNearestNotLooking()

if(not closestChar) then --[[print("closestChar is nil")]] continue end

local pathUnsuccessful = false

local path = pathfinding:CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = false,
	})

local previousPosition = nil

local function reCalculatePath()
	path:ComputeAsync(noob.Torso.Position, workspace[closestChar.Name].HumanoidRootPart.Position)
	if(path.Status == Enum.PathStatus.Success) then
		print("Successfully created the path.")
	else
		print("Path was unsuccessful.")
		pathUnsuccessful = true
	end
end

repeat task.wait(0.03) do		
		reCalculatePath()
		
		if(path.Status == Enum.PathStatus.NoPath) then
			break
		end
		
		local waypoints = path:GetWaypoints()
		
		local waypointToMove = waypoints[2] -- The reason why I'm using the second waypoint is that the first waypoint is mostly the same.
		local alternative = waypoints[1]
		
		if(waypointToMove) then
			noob.Humanoid:MoveTo(waypointToMove.Position)
		else
			noob.Humanoid:MoveTo(alternative.Position)
		end
		
		noob.Humanoid.MoveToFinished:Wait()
		print("MoveTo finished.")
	end
until (not closestChar) or pathUnsuccessful or (module.isLooking(closestChar.Head, noob.Head.Position))  --(not module.isLooking(closestChar.Head, noob.Head.Position))

end
External Media

I’ve also tried using SimplePath which seems to be more consistent with the chasing system I’m working on, it doesn’t have the functionality required for the system.

i watched the video. It seemed like the npc stopped chasing you at some point. Do you know why this is? Could you add in visualization so I can see how it’s pathfinding? Try having it move to the third waypoint instead. Additionally, make some kind of recursive function incase there is no third waypoint.

This also happend to me . The way I counter this is by detecting if the NPC is idle for period of time and just run the path again .

you should not be doing that. that is bad for the game,

If I’m not mistaken, you’re:

  • Computing a path (assuming it succeeds)
  • Pathfinding to waypointToMove (aka waypoints[2])
  • Waiting until waypointToMove is reached
  • Waiting 0.03s
  • (repeat instructions)

Maybe you’re recomputing the path too often? Consider ROBLOX’s example script on their character pathfinding page: Character Pathfinding | Documentation - Roblox Creator Hub
A short snippet of their script shows how they use MoveToFinished to walk to the next waypoint if available, rather than recomputing an entire path everytime:

reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
	if reached and nextWaypointIndex < #waypoints then
		-- Increase waypoint index and move to next waypoint
		nextWaypointIndex += 1
		humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
	else
		reachedConnection:Disconnect()
		blockedConnection:Disconnect()
	end
end)

Consider playing around with this

Basically, computing a path isn’t an instant operation. It yields a bit.
To fix this, don’t use MoveToFinished:Wait(). Just calculate the new path directly after telling the NPC to move. Since computing the path takes a bit of time, the NPC will be moving and calculating the path at the same time creating seamless movement.

Hello and thank you for your response. Why I am updating it way too often is that the player might have moved onto a spot which could be far away from the previous position (aka the position of the player when the NPC started chasing them) so I’m updating it regularly in order to prevent it from being useless. :blush:

Thanks to @hellohennessy, the system seems to be a lot more consistent and the NPC is now able to catch the player without any doubts. I will update the topic if any further issues seem to appear.

I see that I fixed the jiterring issue, but I think that we can add more optimizations.

First, we don’t need to pathfind if the NPC can just move in a straight path. I recommend that you raycast from the NPC to the player and check if there is an obstacle. If there is an obstacle, then you should pathfind. If there are no obstacles, then just use MoveTo()

Next, rather than compute the path every time just in case the player moves, why not only compute the path when the player moves? To do this, check if the distance between the player and the final waypoint is large or not. If the distance is large then it means that the player moved so you should recompute a new path. If the player hasn’t moved, then no need to compute a path.