Unusually high rate on AI and memory leak

The enemy AI for a game i’m trying to make is having an unusually high rate along with memory leakages
To summarize, this is the rate.
Rate is essentially the amount of times scripts run per second, and there usually is about 20 - 50
of these scripts on average so i don’t know why the rate is 60k
image
and activity is also pretty high too, there would also be intense lag along with memory leaks
image

-- Debounces
local Blocked = false
local Pathing = false

-- Essentials
local TargetToMoveTo
local MoveTo
local Waypoint
local print = DebugMode and print or function() end
local CurrentIndex
local PathParams = {AgentHeight = Character:GetModelSize().Y, AgentRadius = Character:GetModelSize().X, AgentCanJump = CanJump, AgentCanClimb = CanClimb, WaypointSpacing = Spacing, Costs = CostConfigs}
local Path:Path = PathfindingService:CreatePath{table.unpack(PathParams)}
local BlockConn

task.spawn(function() -- Get Target
	while task.wait(.2) and Humanoid:GetState() ~= Enum.HumanoidStateType.Dead do
		local Check = GetNearestTarget(Docile and Range.Value or math.huge, Root.Position, false)
		TargetToMoveTo = Check ~= nil and Check or TargetToMoveTo
	end
end)

local function Main(Position, Part:BasePart)
	if Position then
		local Mag = (Root.Position - Position).Magnitude
		if Mag > Range.Value or Part and not CheckSight(Part) then
			local Success = pcall(function() Path:ComputeAsync(Root.Position, Position)  end) -- the current assumption is that waypoints are causing high memory usage
			local CurrentIndex = 0

			if Success and  Path and Path.Status == Enum.PathStatus.Success then
				--print("Success")
				Pathing = true
				Blocked = false
				BlockConn = Path.Blocked:Connect(function(BlockedIndex) -- Make this a one time connection from what i figured
					if BlockedIndex >= CurrentIndex then
						print("Lol blocked")
						Blocked = true
					end 
				end)
				local Points:{PathWaypoint} = Path:GetWaypoints()
				if DebugMode then
					DebugPath(Points)
				end
				task.spawn(function()
					for i, v in Points do
						if not Active.Value or Humanoid:GetState() == Enum.HumanoidStateType.Dead or not Pathing then
							break
						end
						print("Waypointed")
						Waypoint = v
						CurrentIndex = i
						task.wait((16/Humanoid.WalkSpeed)/4)
					end
				end)
			else
				print("Path creation unsuccessful")
			end
		end
	end
end

task.spawn(function() -- Make Path
	repeat
		if TargetToMoveTo and not CheckSight(TargetToMoveTo) then
			--if Main() then
			Main(TargetToMoveTo.Position, TargetToMoveTo)
			local Waits = 0
			local Sight
			repeat
				Sight = CheckSight(TargetToMoveTo)
				Waits += task.wait(.2)
				print("waiting")
			until Waits >= 2 or Blocked or Sight
			if BlockConn then
				BlockConn:Disconnect()
			end
			print("resetting")
			--if Path then
			--	Path:Destroy() -- Destroy it every 2 seconds and create a new one
			--	task.wait()
			--	Path = nil	
			Waypoint = nil
			--end
			--end
			Pathing = false
		else
			task.wait()
		end

	until Humanoid:GetState() == Enum.HumanoidStateType.Dead
end)

local function Clean()
	Waypoint = nil
	for i, v in VisibleWaypoints do
		v:Destroy()
	end
	table.clear(VisibleWaypoints)
	if Path then 
		Path:Destroy() 
	end
	Path = nil
end


Humanoid.Died:Connect(Clean)
Character.Destroying:Connect(Clean)

image

1 Like

Pathfinding service sometimes has memory leaks

I’ve looked at a few posts and there seems to be no solution except to try random things until something works

1 Like

why does pathfinding service have memory leaks?

1 Like

I think its just a bug with roblox

but the memory leaks only happen in weird cases
try doing random stuff to see if it works

1 Like

so i should just contact the roblox support team about this
assuming they’ll care.

2 Likes

Hi! Yea it is a tricky fix I think the best thing to do would be as @happyxander0810 suggested and trying alterative methods to see if it works.

1 Like

i’ve also noticed enemies ramming against the walls instead of going through the entrance that leads to the base
and it seems to happen when the memory leaks are present
And the map is 2 baseplates in size approximately
and pathfinding service accepts a limit of 5000 units
i’ve had the idea of making a path to the mid point of the target incase the target is way too far, such as 15,000 studs, so a path to the mid point will be made which could be in the middle or a different point

1 Like

You could use a pathfinding module

like these ones:

1 Like

how would these be any different if they also use pathfindingservice which is known for having memory leakages?

1 Like

The trip time of Roblox Support to the game engine team is very inefficient. You should make a bug report in #bug-reports.

Please don’t say stuff you aren’t sure about.

You should check the “LuauHeap” category in the Developer Console, it should point out memory leaks in your scripts.


Unorganized assortment of comments on your code:

  • Why are you unpacking and then repacking the table? Also, the radius needs to be half the width, as the full width would be it’s diameter. And, :GetModelSize is deprecated in favour of :GetExtentsSize:

local CharacterSize = Character:GetExtentsSize()

local PathParams = {
    AgentHeight = CharacterSize.Y,
    AgentRadius = CharacterSize.X / 2,
    AgentCanJump = CanJump,
    AgentCanClimb = CanClimb,
    WaypointSpacing = Spacing,
    Costs = CostConfigs
}
local Path:Path = PathfindingService:CreatePath(PathParams)
  • You have a lot of task.spawns and it’s difficult to tell what order your code is running in.

BlockConn = Path.Blocked:Connect(function(BlockedIndex) -- Make this a one time connection from what i figured
	if BlockedIndex >= CurrentIndex then
		print("Lol blocked")
		Blocked = true
	end 
end)

Unless you only compute the path once, there’s no point in having this connection.

Also, you can very easily leak memory with connections:

Why is it in a pcall? If it errors, there’s a serious problem, because I have never seen :ComputeAsync error.

Assuming this is where enemies figure out if they’ve reached a way to progress to the next one, the main issue is that you are blindly assuming the enemy can move in a straight line to a waypoint and takes the exact amount of time to do so each waypoint.

However, there are a lot of cases where the enemy can take a little too much or too little time, and these errors build up. If they get snagged on a wall a little, then they won’t make it all the way to the next waypoint before re-targeting to the one after because they spent some time snagged on a wall.

In all honesty, you should probably re-write this code. Ideally, your pathfinding code should be in a single loop instead of being spread out all over the place (Where do you even call :MoveTo?). But, the easiest way to mitigate this “ramming against the walls” issue, is to have the code actually check if the enemy reached the waypoint, instead of having a timer and hoping for the best.

for i, v in Points do
	if not Active.Value or Humanoid:GetState() == Enum.HumanoidStateType.Dead or not Pathing then
		break
	end
	print("Waypointed")
	Waypoint = v
	CurrentIndex = i
	
	repeat
		task.wait()
	until (Root.Position - Waypoint.Position) < Humanoid.HipHeight * 1.2
end

How often are you calling :ComputeAsync? Realistically, there’s no point calling it more than every half-second.

Maybe your code is duplicating the scripts somehow?

2 Likes

It’s to prevent it from screaming in the output about the path being too long and breaking the code.
The limit is 5000 units.

It is called every 2 seconds or when a path gets blocked, which shouldn’t be very often.

Mainly a helper table that i can customize, also thanks for telling me about width.
I just use getmodelsize because i prefer it, i don’t really care if it’s deprecated

The blocked connection will disconnect every 2 seconds and is meant to force the ai to recompute if the path gets blocked.

I forgot to add it in the code snippet, if you want the full model, here. it is in a loop at the bottom of the code

It doesn’t show in luau heap, and if it does, it looks fine as it shows as 100k size but not 100 million.

I’ve tried checking if the enemy reached the waypoint with waypointreached before and the enemy would stutter a lot, i figured that using a timer was the best.



My god