How can i prevent ai pathfinder from stuttering the longer the game is running

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!

i am working on an ai that patrols and chases players with line of sight, however even without a player, the ai stutters the longer its running

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    and no, dont ask me to use networkowner as i did try setting network and networkownershipauto and looping it for all parts in the ai, it still stutters

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

this is the ai code that is responsible for checking for line of sight and chasing players

local teddy = script.Parent
local humanoid = teddy.Humanoid

local ChaseTorso = nil

teddy.HumanoidRootPart:SetNetworkOwner(nil)


local PathParams = {
	["Agentheight"] = 4,
	["AgentRadius"] = 5,
	["AgentCanJump"] = false
}

local path = game:GetService("PathfindingService"):CreatePath(PathParams)

spawn(function()
	while wait() do
		for i, v in ipairs(script.Parent:GetDescendants()) do
			if v:IsA("BasePart") then
				v.CollisionGroup = "Begulas"
				v:SetNetworkOwner(nil)
				v:SetNetworkOwnershipAuto(nil)
			end
		end
	end
end)

local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local viewRange = 220 -- How far the NPC can see in studs
local fieldOfView = 80 -- The NPC's field of view in degrees
local TargetCharacter = nil
local TargetCharacterPosition = nil

local function canSeeTarget(target)
	local origin = teddy.HumanoidRootPart.Position
	local direction = (target.HumanoidRootPart.Position - teddy.HumanoidRootPart.Position).unit * 60
	local ray = Ray.new(origin, direction)
	
	local hit, pos = workspace:FindPartOnRay(ray, teddy)
	
	
	if hit then
		if hit:IsDescendantOf(target) then
			return true
		end
	else
		return false
	end
end

local function findTarget()
	local players = game.Players:GetPlayers()
	local maxDistance = 125
	local nearestTarget
	
	for index, player in pairs(players) do
		if player.Character then
			local target = player.Character
			local distance = (teddy.HumanoidRootPart.Position - target.HumanoidRootPart.Position).Magnitude
			
			if distance < maxDistance and canSeeTarget(target) then
				nearestTarget = target
				maxDistance = distance
			end
		end
	end
	
	return nearestTarget
end

local function getPath(destination)
	local PathfindingService = game:GetService("PathfindingService")

	
	local path = PathfindingService:CreatePath(PathParams)
	
	path:ComputeAsync(teddy.HumanoidRootPart.Position, destination)
	
	return path
end

local function attack(target)
	local distance = (teddy.HumanoidRootPart.Position - ChaseTorso.Position).Magnitude
	local path = getPath(ChaseTorso)
	if distance > 8 then
	
	if path.Status == Enum.PathStatus.Success then
		for index, waypoint in pairs(path:GetWaypoints()) do
				humanoid.WalkSpeed = 32
				humanoid:MoveTo(waypoint.Position)
			end
		end
	else
		humanoid:MoveTo(ChaseTorso.Position - (teddy.HumanoidRootPart.CFrame.LookVector * 10))humanoid:MoveTo(ChaseTorso.Position)
		humanoid.WalkSpeed = 26
	end
end

local function walkTo(destination)
	-- Compute the path
	
	local timeOut = teddy.Humanoid.MoveToFinished:Wait(1)
	if not timeOut then
		print("attempting to unstuck")
		blockedConnection:Disconnect()
		reachedConnection:Disconnect()
		waypoints = nil
		nextWaypointIndex = nil
		walkTo(destination)
	end
	
	local success, errorMessage = pcall(function()
		path:ComputeAsync(teddy.HumanoidRootPart.Position, destination)
	end)

	if success and path.Status == Enum.PathStatus.Success then
		-- Get the path waypoints
		waypoints = path:GetWaypoints()

		-- Detect if path becomes blocked
		blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
			-- Check if the obstacle is further down the path
			if blockedWaypointIndex >= nextWaypointIndex then
				-- Stop detecting path blockage until path is re-computed
				blockedConnection:Disconnect()
				-- Call function to re-compute new path
				walkTo(destination)
			end
		end)

		-- Detect when movement to next waypoint is complete
		if not reachedConnection then
			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)
		end

		-- Initially move to second waypoint (first waypoint is path start; skip it)
		nextWaypointIndex = 2
		humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
	else
		warn("Path not computed!", errorMessage)
	end
end

function CheckForVisibility(Character)
	local headPosition = teddy.Head.Position
	local objectPosition = Character.Head.Position
	local headCFrame = teddy.Head.CFrame
	local npcToObject = (objectPosition - headPosition).Unit
	local npcLookVector = headCFrame.LookVector
	local dotProduct = npcToObject:Dot(npcLookVector)
	local angle = math.deg(math.acos(dotProduct))
	local distance = (headPosition - objectPosition).Magnitude
	if angle > fieldOfView then
		return false
	end
	if distance > viewRange then
		return false
	end

	if not canSeeTarget(Character) then
		return false
	end
	
	if Character.Hiding.Value then
		return false
	end

	return true
end


spawn(function()
	while wait() do
		wait(math.random(3,7))
		script.Parent.Head:FindFirstChild("Ambient"..math.random(1,4)):Play()
	end
end)


while wait() do
	if TargetCharacter == nil then
		script.Parent.AIMovementBehavior.Enabled = true
		local VisiblePlayers = {}
		local ClosestPlayer = nil
		local Distance = 1e6
		for i, v in ipairs(game.Players:GetPlayers()) do
			if v and v.Character then
				if CheckForVisibility(v.Character) then
					table.insert(VisiblePlayers, v)
				end
			end
		end

		if #VisiblePlayers >= 1 then
			TargetCharacter = VisiblePlayers[math.random(1,#VisiblePlayers)].Character
		end
	else
		if CheckForVisibility(TargetCharacter) then
			TargetCharacterPosition = TargetCharacter.PrimaryPart.Position
		end
		script.Parent.AIMovementBehavior.Enabled = false
		walkTo(TargetCharacterPosition)
		spawn(function()
			if not CheckForVisibility(TargetCharacter) or not getPath(TargetCharacter.PrimaryPart.Position) then
				repeat wait()
					
				until CheckForVisibility(TargetCharacter) or (TargetCharacterPosition - teddy.PrimaryPart.Position).Magnitude <= 6 or not getPath(TargetCharacter.PrimaryPart.Position)
				if not CheckForVisibility(TargetCharacter) or not getPath(TargetCharacter.PrimaryPart.Position) then
					TargetCharacter = nil
				end
			end
		end)
	end
	
	
	
end

this one is responsible for wandering

local teddy = script.Parent
local humanoid = teddy.Humanoid


teddy.HumanoidRootPart:SetNetworkOwner(nil)


local PathParams = {
	["Agentheight"] = 4,
	["AgentRadius"] = 5,
	["AgentCanJump"] = false
}

local path = game:GetService("PathfindingService"):CreatePath(PathParams)

local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection








local function walkTo(destination)
	-- Compute the path
	local success, errorMessage = pcall(function()
		path:ComputeAsync(teddy.HumanoidRootPart.Position, destination)
	end)

	if success and path.Status == Enum.PathStatus.Success then
		-- Get the path waypoints
		waypoints = path:GetWaypoints()

		-- Detect if path becomes blocked
		blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
			-- Check if the obstacle is further down the path
			if blockedWaypointIndex >= nextWaypointIndex then
				-- Stop detecting path blockage until path is re-computed
				blockedConnection:Disconnect()
				-- Call function to re-compute new path
				walkTo(destination)
			end
		end)

		-- Detect when movement to next waypoint is complete
		if not reachedConnection then
			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)
		end

		-- Initially move to second waypoint (first waypoint is path start; skip it)
		nextWaypointIndex = 2
		humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
	else
		warn("Path not computed!", errorMessage)
	end
end

function patrol()
	local waypoints = script.Parent.Parent.Waypoints:GetChildren()
	local randomNum = math.random(1, #waypoints)
	print("Pathfinding")
	
	
	repeat task.wait(.05) 
	walkTo(waypoints[randomNum].Position)
	until (teddy.HumanoidRootPart.Position - waypoints[randomNum].Position).Magnitude <= 6
	print("Reached")
	nextWaypointIndex = nil
	waypoints = {}
	reachedConnection:Disconnect()
	blockedConnection:Disconnect()
end

while wait(0.25) do
	patrol()
end


1 Like

It’s trying to make the humanoid walk across copies of the same path *(or rather, it’s trying to make the humanoid tavel across the same path multiple times over). Try adding a debounce to the patrol function that checks if it’s currently moving toward a goal

local db = false
function patrol()
    if db then return end

	local waypoints = script.Parent.Parent.Waypoints:GetChildren()
	local randomNum = math.random(1, #waypoints)
	print("Pathfinding")
    db = true
	
	
	repeat task.wait(.05) 
	walkTo(waypoints[randomNum].Position)
	until (teddy.HumanoidRootPart.Position - waypoints[randomNum].Position).Magnitude <= 6
	print("Reached")
    db = false
	nextWaypointIndex = nil
	waypoints = {}
	reachedConnection:Disconnect()
	blockedConnection:Disconnect()
end

while wait(0.25) do
	patrol()
end
1 Like