i made it different more performant this way i removed the culling for now i can still add it to not render in chunks behind the camera (depending on how the camera looks figuring out where is behind the camera even if looking down )
local script inside the character
and a module script with the brain
in case someone else need a simple chunk loader
the chunks are just folder containing all the parts and are named like this
“Col: 400|Row:-200|Index:200”
col = X
row = Z
index = Y
grid size is 200x200x200 im not gonna post my map → chunks converter too much effort
local script:
local humanoid = script.Parent:FindFirstChild("Humanoid") or game.Players.LocalPlayer.CharacterAdded:Wait():WaitForChild("Humanoid")--game.Workspace.CurrentCamera
local hrp = script.Parent:WaitForChild("HumanoidRootPart")
local oldPos = nil
local chunkLib = require(script:WaitForChild("chunk"))
--after spawn
chunkLib.run()
--
--FPS
local fps = 0
local timePos = tick()
spawn(function()
while game:GetService("RunService").RenderStepped:wait() do
local difference = math.abs(timePos-tick())
timePos = tick()
fps = math.clamp(math.floor(1/difference),0,60)
end
end)
------
local storedFPSTicks,highestFPS,LowestFPS,averageFPS = {},0,60,0
local chunkRenderDistance = script.chunk.distance
script.chunk.distance.Changed:Connect(function()chunkLib.run()end)
function changeChunkRenderDistance(number)
chunkRenderDistance.Value = math.clamp(chunkRenderDistance.Value + number,2,20)
end
function fpsCheck(fpsATM)
if fpsATM > highestFPS then highestFPS = fpsATM end
if fpsATM < LowestFPS then LowestFPS = fpsATM end
table.insert(storedFPSTicks,fpsATM)
print("FPS",fpsATM,"l:",LowestFPS,"h:",highestFPS,"avg:",averageFPS,"(#checks:",#storedFPSTicks..")","R:",script.chunk.distance.Value)
if #storedFPSTicks > 10 then
table.remove(storedFPSTicks,1)
end
-- calculating average fps from last 10 fpsChecks
local tempAverageFPS = 0
for _,storedFps in pairs(storedFPSTicks) do
tempAverageFPS = tempAverageFPS + storedFps
end
math.floor(((tempAverageFPS / #storedFPSTicks)*10)+.5)/10
averageFPS = tempAverageFPS
if averageFPS < 50 then changeChunkRenderDistance(-1) return end
if averageFPS > 59 then changeChunkRenderDistance(1) return end
end
task.spawn(function()
while wait(2) do
fpsCheck(fps)
end
end)
local debounce = false
humanoid.Running:Connect(function(speed)
while speed > 0 do
wait(1)
if debounce then return end
debounce = true
if not humanoid or not hrp then return end
local newPos = chunkLib.positionToGrid(hrp.Position)
--print("running")
if newPos ~= oldPos then
--print("newPos")
oldPos = newPos
chunkLib.run()
end
debounce = false
end
end)
module script:
local chunk = {}
local visibleChunks = {}
local busyChunks = {}
local chunkStorage = game:GetService("ReplicatedStorage"):WaitForChild("Chunks")
local chunkRender = game:GetService("Workspace"):WaitForChild("Map"):WaitForChild("Chunks")
local chunkSize = 200--game.Workspace:GetAttribute("ChunkSize")
local renderDistance = script:FindFirstChild("distance")
chunk.positionToGrid = function(PositionInput)
return Vector3.new(math.floor(PositionInput.X / chunkSize +0.5) * chunkSize, math.floor(PositionInput.Y / chunkSize +0.5) * chunkSize, math.floor(PositionInput.Z / chunkSize +0.5) * chunkSize)
end
chunk.isBusy = function(chunkName)
if busyChunks[chunkName] then
--print("busy",chunkName)
return true -- chunk is busy
end
--print("free",chunkName)
return false -- chunk is free
end
chunk.reserve = function(chunkName)
--print("reserve",chunkName)
busyChunks[chunkName] = true -- make a free chunk busy
end
chunk.freeUp = function(chunkName)
--print("freedUp",chunkName)
busyChunks[chunkName] = nil -- make a busy chunk free
end
chunk.Load = function(chunkName)
if visibleChunks[chunkName] or chunk.isBusy(chunkName) then
return --skip chunk already doing something
end
if chunkStorage:FindFirstChild(chunkName) then
--print("Load chunk")
chunk.reserve(chunkName)
chunkStorage:FindFirstChild(chunkName).chunkBoundary.grid.Color3 = Color3.fromRGB(0,255,0)
chunkStorage:FindFirstChild(chunkName).Parent = chunkRender
task.wait()
visibleChunks[chunkName] = true
chunk.freeUp(chunkName)
end
end
chunk.Unload = function(chunkName)
if visibleChunks[chunkName] == nil or chunk.isBusy(chunkName) then
return --skip chunk already doing something
end
if chunkRender:FindFirstChild(chunkName) then
--print("Unload chunk")
chunk.reserve(chunkName)
chunkRender:FindFirstChild(chunkName).chunkBoundary.grid.Color3 = Color3.fromRGB(255,0,0)
chunkRender:FindFirstChild(chunkName).Parent = chunkStorage
task.wait()
visibleChunks[chunkName] = nil
chunk.freeUp(chunkName)
end
end
local function renderChunkPoints(centerPosition)
--print("renderPoints",centerPosition)
local newlyRendedChunkPoints = {}
local startX = centerPosition.X - math.floor((renderDistance.Value*chunkSize)+.5)
local endX = centerPosition.X + math.floor((renderDistance.Value*chunkSize)+.5)
local startZ = centerPosition.Z - math.floor((renderDistance.Value*chunkSize)+.5)
local endZ = centerPosition.Z + math.floor((renderDistance.Value*chunkSize)+.5)
local startY = centerPosition.Y - math.floor((renderDistance.Value*chunkSize)+.5)
local endY = centerPosition.Y + math.floor((renderDistance.Value*chunkSize)+.5)
for col = startX,endX,chunkSize do
for row = startZ,endZ,chunkSize do
for index = startY,endY,chunkSize do
if (centerPosition - Vector3.new(col,index,row)).Magnitude <= chunkSize*renderDistance.Value then
local resolvedChunkName = "Col" .. col .. "|Row" .. row .. "|Index" .. index
newlyRendedChunkPoints[resolvedChunkName] = true
end
end
end
end
return newlyRendedChunkPoints
end
local debounce = false
chunk.run = function()
if not debounce then
debounce = true
if not script.Parent.Parent:WaitForChild("HumanoidRootPart") then return end
local chunksPoints = renderChunkPoints(chunk.positionToGrid(script.Parent.Parent:WaitForChild("HumanoidRootPart").Position))
--print("chunkDistance",renderDistance.Value)
for vChunk,_ in pairs(visibleChunks) do
if not chunksPoints[vChunk] then chunk.Unload(vChunk) end
end
for chunkRenderRequest,_ in pairs(chunksPoints) do
if chunkStorage:FindFirstChild(chunkRenderRequest) then chunk.Load(chunkRenderRequest) end
end
task.wait(2)
debounce = false
end
end
return chunk
the fps counter is to dynamicly change render distance from 2 to 20 chunks around the playerCenter chunk
thx for link i gues its solved