How to create infinite voxel terrain without insane lag spikes

I made this quite voxel chunk generation, that loads and unloads chunks as you move around the world. However, there’s insane lag spikes when chunks are loaded in/out, and this is with an incredibly small chunk loading radius (4)

local BLOCK_SIZE = 3 -- Block size
local BLOCKS_PER_CHUNK = 16 -- How many blocks are in each chunk
local CHUNK_SIZE = BLOCK_SIZE * BLOCKS_PER_CHUNK -- Total chunk size in studs

local RENDER_CHUNKS = 4 -- How many chunks should render (/2 either side)

local Lighting = game:GetService("Lighting")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Player = Players.LocalPlayer
local Character = Player.Character
local PlayerGui = Player.PlayerGui

local HUD = PlayerGui.HUD

local RenderedChunks = {}

Lighting.FogEnd = (RENDER_CHUNKS * CHUNK_SIZE) * 0.75
Lighting.FogStart = Lighting.FogEnd * 0.5

local function RenderChunks(chunkX, chunkZ)
	if RenderedChunks[chunkX] and RenderedChunks[chunkX][chunkZ] then
		return
	end
	
	if not RenderedChunks[chunkX] then
		RenderedChunks[chunkX] = {}
	end
	
	local Blocks = {}
	for x = 1, BLOCKS_PER_CHUNK do
		for z = 1, BLOCKS_PER_CHUNK do
			local NewBlock = ReplicatedStorage.Block:Clone()
			NewBlock.Position = Vector3.new(
				(chunkX * CHUNK_SIZE) + (CHUNK_SIZE / 2) + (x * BLOCK_SIZE),
				0.5,
				(chunkZ * CHUNK_SIZE) + (CHUNK_SIZE / 2) + (z * BLOCK_SIZE)
			)
			NewBlock.BrickColor = BrickColor.Random()

			NewBlock.Parent = workspace
			
			local y = 0
			local n = x + y * 16 + z * 16 * 16

			Blocks[tostring(n)] = NewBlock
		end
	end	
	
	RenderedChunks[chunkX][chunkZ] = Blocks	
end

local function UnloadChunksOutsideVicinity(centerX, centerZ)
	for x, chunks in RenderedChunks do
		for z, chunk in chunks do
			if math.abs(x - centerX) > RENDER_CHUNKS / 2 or math.abs(z - centerZ) > RENDER_CHUNKS / 2 then
				for _, block in chunk do
					block:Destroy()
				end
				
				RenderedChunks[x][z] = nil
			end
		end
	end
end

while true do
	local CharacterPosition = Character:GetPivot().Position
	local ChunkX, ChunkZ = 
		math.floor(CharacterPosition.X / CHUNK_SIZE),
		math.floor(CharacterPosition.Z / CHUNK_SIZE)
	HUD.Coordinates.Text = ChunkX .. "," .. ChunkZ

	UnloadChunksOutsideVicinity(ChunkX, ChunkZ)

	for chunkX = ChunkX - (RENDER_CHUNKS / 2), ChunkX + (RENDER_CHUNKS / 2) do
		for chunkZ = ChunkZ - (RENDER_CHUNKS / 2), ChunkZ + (RENDER_CHUNKS / 2) do
			task.spawn(RenderChunks, chunkX, chunkZ)
		end
	end

	task.wait()
end

Roblox video player doesn’t record all the frames, but if you play test there is a noticeable frame drop. This is being run on a relatively high end PC, so it’s not due to limited computing power

I want a way where I can load 16+ chunks without any frame drops between loading/unloading of the chunks. I am unsure of anyway to make this any faster tho, as this is just flat terrain. I imagine having it then load y axis would cause even further strain

Please DO NOT suggest/recommend greedy meshing. This will not work for my use case. I am interested if anyone has used EditableMeshes do any of this kind of stuff and is willing to share. As ideally, I’d only render the top face (vertices) instead of a full block part being rendered

1 Like

All of your script is running on a single thread.
You can try using coroutines or tasks (task.spawn specifically) to optimize your script into using multiple threads, this often helps with performance.

1 Like

Unsure what this is then?? :face_with_raised_eyebrow:

Ok so big thing straight away is you cloning and destroying each time you load a chunk, this is an immediate hit to performance because you could achieve the exact same effect parenting to the workspace and then back to storage.

Secondly, I’m not sure what your specifications are but I would recommend using the Roblox terrain for this it is far more optimised then having 256 instances per chunk and however many chunks your gonna wanna load. It has a learning curve to figuring out how to script it but it’s far better. I have some good examples of procedural generation using Roblox terrain.

The roblox terrain has one huge problem - it’s static. There is no way to destroy specific terrain part. Additionaly, some people do not want their terrain to look like that. That’s why people try to make their own procedural generation (like me)

1 Like