Pathfinding agent stutters after a minute of use

I’m trying to make my dummy’s movement, that I have using Pathfinding Service, to stop stuttering and when a player is near the dummy to target the player instead(targetting a player works perfectly).

After about 1 minute, it goes from perfectly smooth pathfinding to stuttering. When the dummy is chasing a player, it’s fine, but following paths after a minute and it stutters. It’s as if the wait increases drastically between waypoints out of no where.

https://gyazo.com/c207a483ad5411eb90c2e7826f845a09

I’ve found several pages on the devhub all having similar-ish problems but using very different methods. One fix that someone mentioned was runservice.Heartbeat and doing repeat until the dummy is within the waypoints magnitude, but that didn’t solve the issue(maybe somehow I executed that wrong) and even in his video fix it was still moving weird. Another person was using remotes to fire events, and his recommendation was to reduce the amount of times the remote was fired. My dummy is done all serversided so that isn’t something that would be of any use.

local hrp = script.Parent:WaitForChild("HumanoidRootPart")
local dummy = script.Parent
local humanoid = dummy:WaitForChild("Humanoid")

local ii = 0

local function checkforplayer()
	if game.Players then
		for _, plr in pairs(game.Players:GetChildren()) do
			if plr then
				if plr.Character then
					if plr.Character:FindFirstChild("HumanoidRootPart") then
						if plr.Character:FindFirstChild("Humanoid") then
							if plr.Character:FindFirstChild("Humanoid").Health > 0 then
								local phrp = plr.Character:FindFirstChild("HumanoidRootPart")
								if (phrp.Position - hrp.Position).Magnitude < 75 then
									return plr.Character
								end
							end
						end
					end
				end
			end
		end
	end
end



local function path()
	local pathfolder = workspace:WaitForChild("PathFolder2")
	local pathh = pathfolder:GetChildren()
	
	ii = ii + 1
	if ii > #pathh then ii = 1 end
	
	local path = game:GetService("PathfindingService"):CreatePath()
	path:ComputeAsync(hrp.Position,pathh[ii].Position)
	local waypoints = path:GetWaypoints()
	
	if path.Status == Enum.PathStatus.Success then
		for _, waypoint in pairs(waypoints) do
			print(waypoint)
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				humanoid.Jump = true
			end
			local char = checkforplayer()
			if char ~= nil then print(char) return end
			humanoid:MoveTo(waypoint.Position)
			humanoid.MoveToFinished:Wait()
			
		end
		return
	else
		warn("didn't work")
	end

end



local function movetoplayer(char)
	if char then
		if char:FindFirstChild("HumanoidRootPart") then
			
			local phrp = char:FindFirstChild("HumanoidRootPart")
			local path = game:GetService("PathfindingService"):CreatePath()
			path:ComputeAsync(hrp.Position,phrp.Position)
			local waypoints = path:GetWaypoints()
			if path.Status == Enum.PathStatus.Success then
				for _, waypoint in pairs(waypoints) do
					if waypoint.Action == Enum.PathWaypointAction.Jump then
						humanoid.Jump = true
					end
					humanoid:MoveTo(waypoint.Position)
				end
			end
		end
	end
	
end




local function loop()
	while wait() do
		print("stillgoing")
		local playercheck = checkforplayer()
				
		if playercheck ~= nil then print(playercheck, "found")
			movetoplayer(playercheck)
		else
			print(playercheck, "playercheck")
			path()
	
		end
	end
end


loop()
1 Like

I’m going to be honest, I made a path finding module for my game that uses similar system but roblox path finding was not working for me. I suggest you just :GoTo then humanoid position if its open field. Just get the closest player and if its far away then rerun the function. Once you get the HumanoidRootPart just say NPC.Hum:MoveTo(HumanoidRootPart.Position)

Or auto generate nodes based off of the terrain. Dont rely on roblox as it is meant for every game as a whole so it might not work in some cases.

Now that I see the video there is really no point to check with all those conditionals as the script us running in the humanoid.

Just simply use the following:

if  Char:FindFirstChild("Humanoid") then
local NPC = Char:FindFirstChild("Humanoid")
NPC:MoveTo(Node.Position)
end

I would also use collection service and mark the positions like so

local CS = game:GetServer("CollectionService")

for Index, Nodes in ipairs(CS:GetTagged("Nodes")) do
if  Char:FindFirstChild("Humanoid") then
local NPC = Char:FindFirstChild("Humanoid")
NPC:MoveTo(Nodes.Position)
    end
end

link to the plugin is here

1 Like

Thanks for the reply, I figured out the issue. I don’t know why I didn’t think to do this, the issue is still weird and I don’t understand it but the fix is

instead of

if path.Status == Enum.PathStatus.Success then
		for _, waypoint in pairs(waypoints) do
			print(waypoint)
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				humanoid.Jump = true
			end
			local char = checkforplayer()
			if char ~= nil then print(char) return end
			humanoid:MoveTo(waypoint.Position)
			humanoid.MoveToFinished:Wait()
		end
		return
	else
		warn("didn't work")
	end

repeat until but without doing runservice

if path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()
		for _, waypoint in pairs(waypoints) do
			print(waypoint)
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				humanoid.Jump = true
			end
			local char = checkforplayer()
			if char ~= nil then print(char) return end
			humanoid:MoveTo(waypoint.Position)
			repeat 
	        local distance = (waypoint.Position - humanoid.Parent.PrimaryPart.Position).magnitude
	        wait() until distance <= 5
		end
		return
	else
		warn("didn't work")
	end

For whatever reason, MoveToFinished:wait() after 1 minute of running slows down dozens of times over between waypoints.

if anyone has any idea why that is please let me know even though it is still working with this method

4 Likes

It is caused by automatic network ownership. Try to set the agent’s network owner to nil (to the server) with this module:

local function setNetworkOwnerOfBasePart(basePart, networkOwner)
	local success, errorReason = basePart:CanSetNetworkOwnership()
	if success then
		basePart:SetNetworkOwner(networkOwner)
	else
		-- Sometimes this can fail, so throw an error to prevent
		-- ... mixed networkownership in the 'model'
		error(errorReason)
	end
end

local function setNetworkOwnerOfModel(model, networkOwner)
	for _, descendant in pairs(model:GetDescendants()) do
		-- Go through each part of the model
		if descendant:IsA("BasePart") then
			-- Try to set the network owner
			setNetworkOwnerOfBasePart(descendant, networkOwner)
		end
	end
end

local function setNetworkOwner(instance, networkOwner)
	if instance:IsA("Model") then
		setNetworkOwnerOfModel(instance, networkOwner)
	elseif instance:IsA("BasePart") then
		setNetworkOwnerOfBasePart(instance, networkOwner)
	else
		warn(instance.Name .. "'s network ownership cannot be modified.")
	end
end

return setNetworkOwner

Also, see my full explanation here:

2 Likes