Release: 2D Terrain Generation using Perlin Noise!


Its me, the2dguy! If you saw my recent post in #help-and-feedback:cool-creations, I showcased my 2D colored terrain generator, so I thought, why not make it open sourced!



perlin noise (docs) is a cool math algorithm used to produce natural appearing textures which is a Procedural generation algorithm.

For generating terrain, i first started by creating a canvas. The canvas acts as a grid which will have different cells (tiles). Each cell will have its own color. Using perlin noise, we’ll have really cool patterns which will be our terrain! I use os.time() to get randomseeds so you’ll never encounter the same terrain again!

I used a few constant variables:

Vector2 CellSize - Size of each cell in pixels.
number CanvasHeight - Height of the canvas (y)
number CanvasWidth - Width of the canvas (x)
number Scale - Scale is used to determine the size of terrain regions. The closer it is to 0, the bigger regions of terrain we get.

Using the above constant values, using a loop I place the cells in a uniform order, which will form a grid. I color these tiles along the way using perlin noise and seeds! (The cells are congruent squares)


Using the above method, a new terrain map is generated. The colors of each terrain type can also be customized as desired. The amount of land and water can also be customized by editing the source.


Module

I created a module for the same, you can find those below.

The testing place is uncopylocked here for reference:

Example

local Terrain = script.Parent.Terrain -- canvas Frame
local cellsize = Vector2.new(15,15) -- size of each cell
local scale = 0.02 -- for perlin noise pattern scale. the closer it is to 0, the larger the terrain land

local TerrainModule = require(path.to.module)

local newTerrain = TerrainModule.new(Terrain)
newTerrain:gen(cellsize, scale) -- generates the terrain
newTerrain:callback(function()
     print("this is the callback function! this will be printed when the terrain has been generated!")
end)

Source

Thats all from me today.

Hopefully this helps some of you out, I’ll be back with another post regarding forces on a 2D plane soon. See ya next time. :v:

(If you have any questions or feedback, you are free to share them in my DMs or the replies)

5 Likes

i made it real time and from scratch in like 20 minutes

heres code >w<

local res = workspace.CurrentCamera.ViewportSize.X/10
script.Parent.Canvas.UIGridLayout.CellSize = UDim2.fromScale(1/res, 1/res)

local colorbuffer = {}
local offset = Vector2.new(0,0)

local function render()
	for y=1,res do
		for x=1,res do
			local vecCol = colorbuffer[y][x][2]
			local perlin = math.noise((x+offset.X)/res * 10, (y+offset.Y)/res * 10)
			local out
			if perlin < 0 then
				out = Vector3.new(0,1,1)
			elseif perlin < 0.1 then
				out = Vector3.new(233/255, 190/255, 17/255)
			elseif perlin < 1 then
				out = Vector3.new(0,1,0.25)
			end
			colorbuffer[y][x][1].BackgroundColor3 = Color3.new(out.X, out.Y, out.Z)
		end
	end
end

for y=1,res do
	colorbuffer[y] = {}
	for x=1,res do
		local pixel = Instance.new("Frame")
		pixel.BorderSizePixel = 0
		pixel.Parent = script.Parent.Canvas
		colorbuffer[y][x] = {pixel, Vector3.new(x/res, y/res, 0)}
	end
end

game:GetService("RunService").RenderStepped:Connect(function(dt)
	offset += Vector2.new(1,1) * dt
	render()
end)

probably could be a bit faster if you didnt update it every render stepped and just did like while wait(1/30) do

2 Likes