Most efficient way to check for descendants quickly

Hey Developers,

So Im working on a Module which I may call the Optimization module, which is made for optimizing your game. And while yes, it works really well so far, once I got to the distance foliage (Certain foliage appearing depending on distance from it), it started to lag up. My guess is that this is because im always checking the workspace every runservice step. How could I fix this?


local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")

while RunService.RenderStepped:Wait() do
	
	for i,v in pairs(workspace:GetDescendants()) do
		
		if v:IsA("BasePart") then
			
			if CollectionService:HasTag(v, "FoliageDistance") then
				
				if (workspace.CurrentCamera.CFrame.Position - v.Position).Magnitude >= 50 then

					v.Transparency = 1

				else

					v.Transparency = 0

				end
				
			end
			
		end
		
	end
	
end

Seems like your optimization module needs some optimization :stuck_out_tongue:
I don’t think there is a more efficient way to get the descendants, because of the way the function actually works, there would be no way around it. However, you should instead try organizing your foliage instances in a folder and :GetDescendants() from there, that would ease up the load, but you’ll eventually hit a bottleneck when there are too many foliages, then you’ll need to optmize further some way or another.

1 Like

I tried the folder thing right after this post, Haha. Still didnt work, there are around 12k foliage meshes already.

That’s a lot of foliages indeed, I don’t think you have to worry about optmizing in that manner, as in Roblox already stops rendering stuff that’s too far relative to your graphic settings, and you might wanna dig deeper, I think there is a way to force that on a player, but I might be completely wrong.

1 Like

Why check all workspace descendants? You could loop through every part that has the tag instead.

However, how would I get to those parts? 12k of them?

I think the preferred method would be to separate them into “area” tables that correlate to a 100x100 area or something like that, and then only check ones that should logically be checked.

That is, don’t render any of the foliage outside of the area the player is in, and only check the foliage in the area the player is in.

how would I check which area the player is in?

Any number of ways, but the easiest would just be to separate the map into however large chunks with either parts or region3, and then check which one of these the player exists in.

Good idea, but do NOT use region3, that just beats the point of optimizing, I agree with chuncking the map to areas, but using region3 puts you back to square one, as it’s very unoptimized.

region3 is less expensive than :gettouchingparts, but i think the most optimized method would just to check the distance from each area, given that they are all rectangular.

something like

local function IsInside(part)
	local Character = Player.Character or Player.CharacterAdded:Wait()
	local Root = Character:WaitForChild("HumanoidRootPart")
	local difference = (part.CFrame - part.CFrame.p):Inverse() * (Root.Position - part.Position)
	if math.abs(difference.X) < part.Size.X / 2 and math.abs(difference.Z) < part.Size.Z / 2 then
		return true
	end
	return false
end

I was thinking even more efficient, this will take more time to make, but would be better.
What you do is tag each instance with the chunk it’s on, and get which chunck the player is on, and go to the folder where instances that are tagged inside that chunck and render them, the smaller the chuncks the more optimization but the more the checking for adjacent chuncks to render nearby instances too.