Flatlands (biome with no mountains or hills) chance in terrain generator not functional/glitchy

Hello Devforum, today I’m working on my terrain generator again.

I wrote a function that is supposed to occassionally generate some chunks that have no mountains/hills and are just flat (so I can place houses or build towns on them, etc without them being placed at absurd heights/angles),

There is a value between 0 and 1 where 0 will generate no mountains at all and 1 will always generate mountains and hills.

Tried many values and such but I still see hills and mountains appearing when setting it to 0 (never generate mountains/hills).

I have also written some code to do some smooth interportation between hills and no hills so hills/mountains won’t look like as if a chunk was cut off clean.

I feel like the problem lies somewhere here.

mod.randomnoise = function(pos)
	local v = noise(pos.x * 1111.11, pos.y * 1111.11, seed * 11.11)
	if v < 0 then
		v = (1 - v)
	end
	return v
end

local nohillzone = function(pos)
	local pos2 = v3(snap(pos.x, nohillzonedist),0,snap(pos.z, nohillzonedist))
	local ran = mod.randomnoise(pos2)
	local dist = (pos - pos2).magnitude
	
	local offset = (dist / 10) * 9.5
	local lerpdist = (dist / 10) * 0.5
	
	if ran > nohillzonechance and dist > offset then
		return (nohillzonedist - offset) / lerpdist
	elseif ran > nohillzonechance and dist < offset then
		return 0
	end
	return 1
end


mod.heightnoise = function(pos) --Basic terrain generation
	pos = pos + posoffset
	local n1 = noise(pos.x / steepness, pos.z / steepness, seed) * amp
	local n2 = noise(seed, pos.z / steepness2, pos.x / steepness2)
	return (n1 * n2) * nohillzone(pos)
	
--	local n1 = noise(pos.x / steepness, pos.z / steepness, seed)
--	local n2 = n1 * noise(seed, pos.z / steepness2, pos.x / steepness2)
--	if (n1) > 0 then
--		n1 = lerp(0, amp, n1)
--		return n1
--	end
--	return 0
end

But here is the whole module in case you need to read more of it like what variables I’m using, etc.

--==[[ Configurations ]]==--
local seed = 801429683 * 0.08
local posoffset = Vector3.new(0, 0, 0) --Center of the terrain.


local amp = 1000
local steepness = 1000
local steepness2 = 2000
local nohillzonechance = 0 --0 always, 1 never.

local biomechancemultiplier = 2
local biomesize = 10000
local biomerarity = 2000
--Advanced
local biomesnap = 0.5
local biomes = {}
biomes["1"] = {name = "rocklands";
		mat1 = Enum.Material.Rock;
		mat2 = Enum.Material.Ground;
		}
biomes["0.5"] = {name = "desert";
		mat1 = Enum.Material.Sand;
		mat2 = Enum.Material.Ground;
		}
biomes["0"] = {name = "default";
		mat1 = Enum.Material.Grass;
		mat2 = Enum.Material.Mud;
		}
biomes["-0.5"] = {name = "snow";
		mat1 = Enum.Material.Snow;
		mat2 = Enum.Material.Glacier;
		}
biomes["-1"] = biomes["1"]
--==[[ No touchy below. ]]==--

local nohillzonedist = 1000

local noise = math.noise
local floor = math.floor
local clamp = math.clamp
local v3 = Vector3.new

local mod = {}

local function lerp(a, b, c)
	return a + (b - a) * c
end

local function snap(x, y)
	return floor((x / y) + 0.5) * y
end



mod.randomnoise = function(pos)
	local v = noise(pos.x * 1111.11, pos.y * 1111.11, seed * 11.11)
	if v < 0 then
		v = (1 - v)
	end
	return v
end

local nohillzone = function(pos)
	local pos2 = v3(snap(pos.x, nohillzonedist),0,snap(pos.z, nohillzonedist))
	local ran = mod.randomnoise(pos2)
	local dist = (pos - pos2).magnitude
	
	local offset = (dist / 10) * 9.5
	local lerpdist = (dist / 10) * 0.5
	
	if ran > nohillzonechance and dist > offset then
		return (nohillzonedist - offset) / lerpdist
	elseif ran > nohillzonechance and dist < offset then
		return 0
	end
	return 1
end


mod.heightnoise = function(pos) --Basic terrain generation
	pos = pos + posoffset
	local n1 = noise(pos.x / steepness, pos.z / steepness, seed) * amp
	local n2 = noise(seed, pos.z / steepness2, pos.x / steepness2)
	return (n1 * n2) * nohillzone(pos)
	
--	local n1 = noise(pos.x / steepness, pos.z / steepness, seed)
--	local n2 = n1 * noise(seed, pos.z / steepness2, pos.x / steepness2)
--	if (n1) > 0 then
--		n1 = lerp(0, amp, n1)
--		return n1
--	end
--	return 0
end

mod.biomenoise = function(pos) --Biome selection and generation
	pos = pos + posoffset
	local n1 = noise(pos.z / biomesize, pos.x / biomesize, seed * 2)
	local r = clamp(n1 * biomechancemultiplier, -1, 1)
	
	r = snap(r, biomesnap)
	
	local selbiome = biomes[tostring(r)]
	return selbiome.mat1
end



return mod

I’d be super happy if I can get this to work.

I’m trying to make a terrain generator that soon might also generate buildings, lakes, maybe large bridges that stretch over big lakes, basically entire cities and some nature parts likes forests, etc.

Eventually this module is supposed to generate entire (and infinite) maps for open-world post-apocalypse games where buildings are procedurally generated with random rooms, furniture as well perhaps (I’ll see how far I’ll go) with random loot, etc.

2 Likes

Edit: I managed to get closer to what I want to achieve but at this point I’m really getting stuck since I really just tried something random and sorta worked with some dumb luck.

But I’m now getting this as a result.

Code below.

--==[[ Configurations ]]==--
local seed = 801429683 * 0.08
local posoffset = Vector3.new(0, 0, 0) --Center of the terrain.


local amp = 1000
local steepness = 1000
local steepness2 = 2000
local nohillzonechance = 0.2 --0 never, 1 always.

local biomechancemultiplier = 2
local biomesize = 10000
local biomerarity = 2000
--Advanced
local biomesnap = 0.5
local biomes = {}
biomes["1"] = {name = "rocklands";
		mat1 = Enum.Material.Rock;
		mat2 = Enum.Material.Ground;
		}
biomes["0.5"] = {name = "desert";
		mat1 = Enum.Material.Sand;
		mat2 = Enum.Material.Ground;
		}
biomes["0"] = {name = "default";
		mat1 = Enum.Material.Grass;
		mat2 = Enum.Material.Mud;
		}
biomes["-0.5"] = {name = "snow";
		mat1 = Enum.Material.Snow;
		mat2 = Enum.Material.Glacier;
		}
biomes["-1"] = biomes["1"]
--==[[ No touchy below. ]]==--

local nohillzonedist = 100

local noise = math.noise
local floor = math.floor
local clamp = math.clamp
local v3 = Vector3.new

local mod = {}

local function lerp(a, b, c)
	return a + (b - a) * c
end

local function snap(x, y)
	return floor((x / y) + 0.5) * y
end



mod.noiserandom = function(pos)
	local v = noise(pos.x / -12345.6, pos.z / -12345.6, seed * 121.12)
	if v < 0 then return 1 + v end
	return v
end

local nohillzonesnap = function(pos)
	return v3(
		floor((pos.x / nohillzonedist) + 0.5) * nohillzonedist,
		pos.y,
		floor((pos.z / nohillzonedist) + 0.5) * nohillzonedist
		)
end

local nohillzone = function(pos)
	local pos2 = nohillzonesnap(pos)
	local ran = mod.noiserandom(pos2)
	
	local dist = (pos - pos2).magnitude
	local offset = (nohillzonedist / 10) * 9
	local lerpdist = nohillzonedist - offset
	
	if ran > nohillzonechance or dist > nohillzonedist then return 1 end
	if dist > offset then
		return (dist - offset) / lerpdist
	elseif dist < offset then
		return 0
	end
	return 1
end


mod.heightnoise = function(pos) --Basic terrain generation
	pos = pos + posoffset
	local n1 = noise(pos.x / steepness, pos.z / steepness, seed) * (amp * nohillzone(pos))
	local n2 = noise(seed, pos.z / steepness2, pos.x / steepness2)
	return n1 * n2
end

mod.biomenoise = function(pos) --Biome selection and generation
	pos = pos + posoffset
	local n1 = noise(pos.z / biomesize, pos.x / biomesize, seed * 2)
	local r = clamp(n1 * biomechancemultiplier, -1, 1)
	
	r = snap(r, biomesnap)
	
	local selbiome = biomes[tostring(r)]
	return selbiome.mat1
end



return mod



https://gyazo.com/4cad9ecc81b471dfc571662f21419f6a

I somehow ended up doing this which is the complete opposite.
I have tried many things by now, somehow unable to get it working.

I tried something with distance that if the distance would be greater than/less than a certain magnitude I try to lerp the values for smooth interportation between flat terrain and terrain with mountains and hills but ended up glitching the entire terrain generator and making it incredibly unstable somehow.

Right now it looks like you’ve set some places to be flat and some places to be hilly, however you’ve not told it how to smoothly transition between the two, hence what you’re getting here

1 Like

Well, yes, I am trying to have hilly and flat places.
I have told it to do a smooth interportation between the two but it keeps doing anything but what it’s supposed to do.

I have tried this now (I tried like 20 different things already, I feel like the lerp part is done wrong or something else).

local nohillzonesnap = function(pos)
	return v3(
		floor((pos.x / nohillzonedist) + 0.5) * nohillzonedist,
		pos.y,
		floor((pos.z / nohillzonedist) + 0.5) * nohillzonedist
		)
end

local nohillzone = function(pos)
	local pos2 = nohillzonesnap(pos)
	local ran = mod.noiserandom(pos2)
	
	local dist = (pos - pos2).magnitude
	local offset = (nohillzonedist / 10) * 9
	local lerpdist = nohillzonedist - offset
	
	if ran > nohillzonechance or dist > nohillzonedist then return 1 end
	if dist < offset then return 0 end
	return lerp(1, 0, (dist - offset) / lerpdist)
end


mod.heightnoise = function(pos) --Basic terrain generation
	pos = pos + posoffset
	local n1 = noise(pos.x / steepness, pos.z / steepness, seed) * (amp * nohillzone(pos))
	local n2 = noise(seed, pos.z / steepness2, pos.x / steepness2)
	return n1 * n2
end

And I keep getting this.

https://gyazo.com/7e474c475a2baf1fbc6ce5f49c5b3889

Edit: For clarification nohillzonedist is the size of each “Flatlands biome”, bigger values give bigger pieces of terrain with flat terrain, but this is not where the mistakes are being made at.

I know the mistake lays somewhere in the flatzone function, a wrong calculation.