Well, for me this is how I set up my system:


Inside the “render space 1”, you can see all the parts that are being rendered/unrendered (folder called “Parts”). The origin part is the position in which it decides the players distance from.
Upon start, the client gets all the render spaces, and stores each one as a table.
For each render space it stores the parts origin, then I use :GetDescendants on the “Parts” folder, check to make sure each piece is a basepart, then insert it into a table.
local runService = game:GetService("RunService")
local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera
local renderFolder = workspace._Render
local renderTable = {}
local step = 0
for _,v in pairs(renderFolder:GetChildren()) do -- v is the render space
local originPos = v.Origin.Position
local renderDistance = v.Origin:GetAttribute("RenderDistance") or 250 -- each origin has an attribute for its render distance, if not found then make it 250
local parts = {} -- where we will store all the parts for that render space
for _,part in pairs(v.Parts:GetDescendants()) do
if part:IsA("BasePart") then
table.insert(parts,part) -- put it into the table "parts"
end
end
table.insert(renderTable,{
originPos;
renderDistance;
parts;
true; -- this is used to decide if its rendered or not
}) -- insert this into the main table, this contains all the data for this render space.
end
Then, I loop through every render space, and check the cameras distance from the render space’s origin, and see if its close enough to render it. If close enough, then render the parts (if not already rendered), else unrender the parts (if not already unrendered).
runService.Heartbeat:Connect(function()
---
step += 1
if step < 8 then
return
end
step = 0
--- this is so everything below only goes off every 8 heartbeats
local cameraPosition = camera.CFrame.Position -- camera position
for _,list in pairs(renderTable) do -- gets all render spaces
local distance = (cameraPosition - list[1]).Magnitude -- finds cameras distance from render space's origin
if distance <= list[2] then -- if distance is less than the render spaces render distance then
if list[4] == false then --if not already rendered then
for _,part in pairs(list[3]) do -- gets all parts from render space
part.LocalTransparencyModifier = 0 -- set local transparency modifier to zero, so now it is render
end
end
list[4] = true -- seen as how it is now render, update the list to say this
elseif distance > list[2] then -- if distance is FARTHER than the render spaces render distance then
if list[4] == true then -- make sure its not already unrendered
for _,part in pairs(list[3]) do -- get all render spaces part
part.LocalTransparencyModifier = 1 -- make part invisible/unrender it
end
end
list[4] = false -- update table to say that now this render space is unrendered
end
end
end)
Here’s my full script:
task.wait(1)
if not game.Loaded then
game.Loaded:Wait()
end
local runService = game:GetService("RunService")
local player = game.Players.LocalPlayer
local camera = workspace.CurrentCamera
local renderFolder = workspace._Render
local renderTable = {}
local step = 0
for _,v in pairs(renderFolder:GetChildren()) do
local originPos = v.Origin.Position
local renderDistance = v.Origin:GetAttribute("RenderDistance") or 250
local parts = {}
for _,part in pairs(v.Parts:GetDescendants()) do
if part:IsA("BasePart") then
table.insert(parts,part)
end
end
table.insert(renderTable,{
originPos;
renderDistance;
parts;
true; -- is rendered on start
})
end
runService.Heartbeat:Connect(function()
step += 1
if step < 8 then
return
end
step = 0
local cameraPosition = camera.CFrame.Position
for _,list in pairs(renderTable) do
local distance = (cameraPosition - list[1]).Magnitude
if distance <= list[2] then
if list[4] == false then
for _,part in pairs(list[3]) do
part.LocalTransparencyModifier = 0
end
end
list[4] = true
else
if list[4] == true then
for _,part in pairs(list[3]) do
part.LocalTransparencyModifier = 1
end
end
list[4] = false
end
end
end)
I hope this helps! My script is not perfect so I only advise you to take reference from it. This also does not unrender textures/decals, just baseparts.
Extra Tips:
- Another way you could get all the render spaces is collection service
- I suggest trying to keep the amount of render spaces limited
- if you want to have it update less, change the 8 to a higher number. Lower it to have it update faster (but this makes it less performant)
If you’re wondering why I use LocalTransparencyModifier instead of transparency, is because its kind of like an override to normal transparency. Here’s more about it: BasePart.LocalTransparencyModifier