Help optimizing pathfinding requests

Recently, I’ve been working on an enemy pathfinding system for a COD style game. I’ve managed to finish the system in a way that’s both optimized and very fast, but when stress testing with dozens of enemies, I noticed that, the longer the game ran for, the slower the pathfinding system would update each enemy. Their paths start refreshing much, much slower, causing them to freeze for a long time before finding a new path. I don’t know exactly why, given no errors or warnings appear, but I have a strong suspicion it’s because of the amount of path requests I make in a short amount of time.

I want to ask: What are some ways I can cut down on path refreshes? I’m already skipping a refresh if enemies are too far away or if the target hasn’t moved a significant distance. I’ve read about caching paths, but I’m not sure how I’d implement that, given enemies need to chase players up close.

--Code snippet relevant to pathfinding requests. The Pathfinding itself is a simple Pathfinding:CreatePath() and Pathfinding:ComputeAsync() system.

local UpdatesPerSecond = 4
local Interval = 1 / UpdatesPerSecond

local accumulator = 0

local function PathfindingRefreshAllowed(Origin : Vector3, LastTarget : Vector3) : boolean
	local closestPlayer = nil
	local closestDistance = nil
	
	for i, Plrs in Players:GetPlayers() do
		if Plrs.Team == Teams.Dead or not Plrs.Character then continue end
		
		local primaryPart = Plrs.Character.PrimaryPart
		
		local PlayerPos = Vector3.new(primaryPart.Position.X, Origin.Y, primaryPart.Position.Z)
		
		local dist = (PlayerPos - Origin).Magnitude
		
		if not closestPlayer or dist < closestDistance then
			closestPlayer = PlayerPos
			closestDistance = dist
		end
	end
	
	if not closestDistance then return end
	
	if closestDistance > 60 then
		--print("Closest player is too far away. Path cannot refresh.")
		return false
	end
	
	if LastTarget and (LastTarget - closestPlayer).Magnitude <= 2 then
	--	print("Target is too to the last check. Path cannot refresh.")
		return false
	end
	
	return true
end

-- MAIN ENEMY PATHFINDING
RunService.Heartbeat:Connect(function(DT)
	accumulator += DT
	
	for ID, info in EnemyStorage.GetEnemies() do
		if #info.Waypoints == 0 then
			EnemyPathFinder(info.Position, ID)
			continue
		end
		
		local currentPosition = info.Position
		local goal = info.Waypoints[1]
		
		local speed = info.Speed
		local direction = (goal - currentPosition).Unit
		
		local newPosition = currentPosition + direction * speed * DT
		
		EnemyStorage.Move(ID, newPosition, info.DebugPart)
	end

	while accumulator >= Interval do
		accumulator -= Interval
		
		module.UpdateAllPaths()
		module.ClientSend()
	end
end)

function module.UpdateAllPaths()
	for ID, info in EnemyStorage.GetEnemies() do
		if not PathfindingRefreshAllowed(info.Position, info["LastTarget"]) then continue end

		EnemyPathFinder(info.Position, ID)
	end
end

Thanks in advance!

1 Like

Just looking briefly at your code, I’m going to bet the lag is coming from having the while loop inside of the RunService connection. Can you move that outside the connection?

why’s there no scheduler (task.wait()) in the while loop which is also in a heartbeat function?? this will create a while loop every single frame, and this will REALLY cause performance issues

It seems to have helped a little (i might be a bit silly for missing that), but it doesn’t completely fix the issue, no. Paths still eventually take ~10 seconds to refresh if the game goes on for long enough.

if the performance gets visibly worse as the server ages, it means you’ve got memory leaks, which will basically ruin your game. Pathfinding is also pretty performance expensive, so try lowering the updates per second and see if it improves the lag a bit

Do u set your units or characters who use pathfinding this?

local hrp = self.Model:FindFirstChild(“HumanoidRootPart”)
hrp:SetNetworkOwner(nil) --Avoid Pathfinding Lag