Lagless infinite terrain generator

I was poking around with roblox studio, and took another shot at making a terrain generator. The last time i tried doing this, roblox had performance issues generating the terrain, this goes to show how much effort the developers put into optimizing roblox! thanks roblox devs for being so persistent with optimization improvements!

Anyways, here is the code, if anyone wants it, just insert a script into the workspace, or server script service, then put this into the script.

--Chunk generator by rater193
--The chunk generator settings
local settings = {
	chunkSize = 128,
	renderDistance = 6
}
--Chunk generation
--This stores the list of chunks queued to generate terrain
chunksToGenerate = {}

--This handles queuing new chunks to generate
function queueChunk(x, y)
	table.insert(chunksToGenerate, {["x"] = x, ["y"] = y})
end

--Here we are tapping into the runservice stepped event to process queued chunks to generate
game:GetService("RunService").Stepped:connect(function()
	--Only run if we have chunks to generate
	if(#chunksToGenerate>0) then
		--Here we are retreiving the first item in the list
		local item = chunksToGenerate[1]
		
		--Then we remove it from the list, and process it
		table.remove(chunksToGenerate, 1)
		
		--Here we are setting the chunk's part to render green, to show that we have generated the chunk(for debugging)
		chunks[getChunkID(item.x, item.y)].part.BrickColor = BrickColor.new(Color3.fromRGB(0,255,0))
		
		--Here are some additional reused values for generating the terrain
		local chunkgeneratorsettings = {
			["xstart"] = item.x*settings.chunkSize,
			["ystart"] = item.y*settings.chunkSize,
			["width"] = math.ceil(settings.chunkSize/4),
			["height"] = math.ceil(settings.chunkSize/4)
		}
		
		--Here we are generating the terrain
		for _x = 0,chunkgeneratorsettings.width do
			for _y = 0,chunkgeneratorsettings.height do
				
				--Actual terrain generation logic
				local _height = (math.noise(((_x*4)+chunkgeneratorsettings.xstart)/1000,((_y*4)+chunkgeneratorsettings.ystart)/1000)+0.5) * 50
				local _material = Enum.Material.Air


				if(_height<1) then
					_height = 1
					_material = Enum.Material.Water
				elseif(_height<3) then
					_material = Enum.Material.Sand
				else
					_material = Enum.Material.Grass
				end

				game.workspace.Terrain:FillBlock(CFrame.new((_x*4)+chunkgeneratorsettings.xstart,_height/2,(_y*4)+chunkgeneratorsettings.ystart), Vector3.new(1,_height,1), _material)
			end
		end
	end
end)

--Now we are going to start handling chunks loading in around the players
--Chunk management
--The list of generated chunks
chunks = {}
--This gets the id in the list for the chunk
function getChunkID(x, y)
	return tostring(x)..","..tostring(y)
end

--This checks if we need to generate the chunk
function checkChunk(x, y)
	local chunkID = getChunkID(x, y)
	if(chunks[chunkID]==nil) then
		chunks[chunkID] = {}
		
		--Here we are generating a preview part, to let us know what chunks we are trying to generate(for debugging)
		local part = Instance.new("Part")
		part.Anchored = true
		part.CanCollide = true
		part.BrickColor = BrickColor.new(Color3.fromRGB(255,0,0))
		part.Transparency = 0.5
		part.Material = Enum.Material.Grass
		part.Size = Vector3.new(settings.chunkSize, 1, settings.chunkSize)
		part.Position = Vector3.new(settings.chunkSize * x, 0, settings.chunkSize * y) + Vector3.new(settings.chunkSize/2,0,settings.chunkSize/2)
		
		part.Parent = game.Workspace
		
		part.Name = "Chunk-"..tostring(x).."."..tostring(y)
		
		--Adding the preview part to the chunk data
		chunks[chunkID].part = part
		
		--Finally, queue the chunk for terrain generation
		queueChunk(x,y)
	end
end

--Here we are tapping into the run service stepped event to handle chunks around the players
game:GetService("RunService").Stepped:connect(function()
	for i, player in pairs(game.Players:GetPlayers()) do
		if(player.character and player.character:findFirstChild("Head")) then
			local _pos = {
				["x"]= math.floor((player.character.Head.Position.X)/settings.chunkSize),
				["y"]= math.floor((player.character.Head.Position.Z)/settings.chunkSize)
			}
			

			for _x = _pos.x-settings.renderDistance, _pos.x+settings.renderDistance do
				for _y = _pos.y-settings.renderDistance, _pos.y+settings.renderDistance do
					checkChunk(_x, _y)
				end
			end
		end
	end
end)

Enjoy the script!

33 Likes

This is so cool. But if you had it able to generate materials other than grass, I can see it being a great system to have to have infinitely generated terrain like minecraft.

1 Like

i made a crude terrain generator, i do plan on modifying it to support biome generation similar to minecraft.

8 Likes

Would it be possible implement a mesh / tree placement system in this model?

1 Like

Yes, you just have to implement it into the generator itself, and place it based off the heightmap it is using(idealy)

you awnsered half a year later? what-

Thanks for responding but i gave up on that project so yeah

Looks like biome support is going “great”

1 Like

yeah, my account was locked out, due to me enabling MFA, and switching phones without migrating the MFA codes. It took me that long just to get my account back xD

There are a lot of newer methods i would love to tinker with for generating biomes. Microsoft released a really detailed youtube video explaining how generation is SUPPOSED to work. I want to give a crack at some point re-creating the generator to match exactly how minecraft does their terrain generation.

Well, if you ever do good luck! Can I also have a link to the vid?

Sure.

Its really insightful, and i definitely want to re-create it at some point :slight_smile:
While it doesnt go into detail on the algorithm exactly, it does give you an idea of how it works, and also a general idea of how you can implement it inside roblox.

1 Like

I have a question and I want your thoughts. A year or two ago I used your method of storing chunks in tables, but the game was a bit laggy once a lot were loaded because it kept having to iterate over a lot more in the table. I’m trying again fresh, and rather than using a table I have invisible parts that are triggered based on using GetPartBoundsInRadius() on each player (that way it only iterates through the nearest chunks). Do you think this method of doing it would be faster, or not worth it due to constantly creating an invisible part for each chunk in the terrain?