Best way to finish this Pathfinding script to prevent NPC stuttering?

	local path = PathfindingService:CreatePath()
	path:ComputeAsync(root.Position, target.Position)
	if path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints(npcRadius, npcHeight)
		
		for _, waypoint in pairs(waypoints) do	
			humanoid:MoveTo(waypoint.Position)--, target)
			humanoid.MoveToFinished:Wait()
		end
	end

It’s so close to working. I can’t seem to find the best way to have the NPC dynamically respond to my path whilst not being horribly outdated or stuttery. It is following a player torso.

Any nudges in the right direction? I am aware of the blocked path method.

2 Likes

Could it be a network ownership issue by any chance? Or is this already local?

Hm?

This is serverside if that’s what you mean.

What exactly do you mean by “NPC stuttering”?

At the :MoveToFinished:Wait() the NPC pauses uncomfortably while waiting for the next node/change in path.

I get why it does that, I’m just not sure how best to do it differently.

I’ve had problems using :MoveToFinished in the past, and also recently (when I wanted to revisit this problem). I had many NPC stuttering issues. Ultimately, setting the NPC’s PrimaryPart to server ownership and using this code worked for me

1 Like

You can check for distance instead of using the signal.
For example:

local path = PathfindingService:CreatePath()
path:ComputeAsync(root.Position, target.Position)
if path.Status == Enum.PathStatus.Success then
    local waypoints = path:GetWaypoints(npcRadius, npcHeight)
    for _, waypoint in pairs(waypoints) do	
	humanoid:MoveTo(waypoint.Position)--, target)
	repeat wait() until (root.Position - target.Position).Magnitude <= 0.2
    end
end

Please excuse the horrible indenting and stuff I’m on mobile

1 Like

These solutions are close, but they seem to break if the NPC can never get close to the proper position (such as if the map changes).

I believe you could connect to the path.Blocked signal setting a certain variable to true, then in your distance loop you could check if that variable is true and then break the loop

I’m having a bit of trouble getting path.Blocked to fire. Any chance you could provide an example?

Here’s what I’m attempting:

path.Blocked:Connect(function()
	print("BLOCK!")
	breakPath = true
end)

You can constantly check the magnitude of the humanoid to the target goal to check if the humanoid has reached the target goal, or is at least near the target if it stutters.

local path = PathfindingService:CreatePath()
path:ComputeAsync(root.Position, target.Position)
if path.Status == Enum.PathStatus.Success then
	local waypoints = path:GetWaypoints(npcRadius, npcHeight)
	for _, waypoint in pairs(waypoints) do	
		repeat
		humanoid:MoveTo(waypoint.Position)--, target)
		until (HumanoidRootPart.Position - waypoint.Position).magnitude < 2 ---You can increase this if you want to check for larger distances.
		---You can then teleport the Character to the target position to prevent issues with reaching the target goal due to stuttering.
	end
end

Edit: My mistake, I haven’t properly read this whole forum discussion, my answer is similar to what @Zooleos has posted in Post#7, however in my experience repeating :MoveTo() just ensures that the NPC doesn’t stop walking.

2 Likes

Yeah, MoveToFinished has a weird yield when used in a loop like this, especially with pathfinding.

Can I use Path.Blocked for it to know if the path changes to be impossible?

A similar issue was posted a few months ago:

You might find this helpful, the solution might be a simple network ownership disagreement. Network ownership is responsible for setting which machine will manage the physics of a certain object (velocity, angular velocity, position, etc), basically whatever the machine sees how the physics should be, that data will be replicated to the whole server and other clients. If this is not properly managed, it may cause the stuttering that you are observing and exploit vulnerability.

Additionally, network ownership by default will let the server manage the physics of an object until a player comes within a certain distance to that object (the player now claims temporary ownership of that object).

1 Like

Pretty sure you can, just break the loop when it detects a block.

repeat
if breakPath == true then break end
humanoid:MoveTo(waypoint.Position)--, target)
until (HumanoidRootPart.Position - waypoint.Position).magnitude < 2 ---You can increase this if you want to check for larger distances.

path.Blocked:Connect(function()
	print("BLOCK!")
	breakPath = true
end)
1 Like

This will stop the script if the patch is blocked.

local path = PathfindingService:CreatePath()
path:ComputeAsync(root.Position, target.Position)
if path.Status == Enum.PathStatus.Success then
breakpath = false
local waypoints = path:GetWaypoints(npcRadius, npcHeight)
for _, waypoint in pairs(waypoints) do	
	repeat
    if breakPath == true then break end
	humanoid:MoveTo(waypoint.Position, waypoint)--, target)
    wait(0.1)
	until (HumanoidRootPart.Position - waypoint.Position).magnitude < 4 or breakPath == true
end
end

path.Blocked:Connect(function()
    breakPath = true
end
1 Like

I’m extremely close, however sometimes the NPC gets stuck walking into a wall. It doesn’t seem to think the path is impossible or blocked?

Apparently the NPC thinks the best path is to go a few feet into the ground??

I resolved this by implementing a short countdown to prevent it from getting stuck on an impossible path forever.

Thank you again for all the help!

local path = PS:CreatePath(pathParams)

--before while loop
path.Blocked:Connect(function()
	print("Break!")
    breakPath = true
end)

--in while loop
path:ComputeAsync(root.Position, target.Position)
if path.Status == Enum.PathStatus.Success then
	breakPath = false
				
	local waypoints = path:GetWaypoints(pathParams)
        local wastedTime = 0
	for _, waypoint in pairs(waypoints) do	
		repeat
                        wastedTime = wastedTime + 1
			if(waypoint.Action == Enum.PathWaypointAction.Jump) then
				human.Jump = true
			end
						
			 if breakPath == true then break end
			human:MoveTo(waypoint.Position)
			wait()
		until (root.Position - waypoint.Position).magnitude < 8 or breakPath == true or wastedTime >= 50
	end
end
6 Likes