I am making a top-down 2D game, and am generating “flat” worlds with many different biomes. I’ve got the script finished, it works, but the results look really unnatural.
This is my first time working with procedural generation, so I’m not sure if I’m applying the most efficient methods for everything.
What can I do to improve the overall look of how it generates?
Here’s my code:
local biomeInfo = require(script.BiomeInfo)
local heightSeed = math.random(-2147483640, 2147483640)
local moistureSeed = math.random(-2147483640, 2147483640)
local heatSeed = math.random(-2147483640, 2147483640)
local mapSize = 250
local resolution = 20
local frequency = 15
local amplitude = 95
-- Generate noise maps
local grid = {}
for x = 1, mapSize do
grid[x] = {}
for z = 1, mapSize do
local heightMap = math.noise(x / resolution * frequency / amplitude, z / resolution * frequency / amplitude, heightSeed)
local moistureMap = math.noise(x / resolution * frequency / amplitude, z / resolution * frequency / amplitude, moistureSeed)
local heatMap = math.noise(x / resolution * frequency / amplitude, z / resolution * frequency / amplitude, heatSeed)
--[[local heightMap = math.noise(x / resolution * 1.2 / 3.2, z / resolution * 1.2 / 3.2, heightSeed)
local moistureMap = math.noise(x / resolution * 4 / 5.6, z / resolution * 4 / 5.6, moistureSeed)
local heatMap = math.noise(x / resolution * 2.7 / 7, z / resolution * 2.7 / 7, heatSeed)]]
grid[x][z] = {
["Height"] = math.clamp(heightMap, -0.5, 0.5) + 0.5,
["Moisture"] = math.clamp(moistureMap, -0.5, 0.5) + 0.5,
["Heat"] = math.clamp(heatMap, -0.5, 0.5) + 0.5,
}
end
end
-- Generate blocks
for x = 1, mapSize do
for z = 1, mapSize do
-- Get all available biomes for this block
local availableBiomes = {}
for name, value in pairs(biomeInfo) do
local condition = grid[x][z]["Height"] >= biomeInfo[name]["Height"] and grid[x][z]["Moisture"] >= biomeInfo[name]["Moisture"] and grid[x][z]["Heat"] >= biomeInfo[name]["Heat"]
if condition and not availableBiomes[name] then
table.insert(availableBiomes, name)
end
end
-- Calculate value differences for all available biomes for this block
local valueDiffs = {}
for _, biome in pairs(availableBiomes) do
valueDiffs[biome] = (grid[x][z]["Height"] - biomeInfo[biome]["Height"]) + (grid[x][z]["Moisture"] - biomeInfo[biome]["Moisture"]) + (grid[x][z]["Heat"] - biomeInfo[biome]["Heat"])
end
-- Get the lowest value difference, assign the corresponding biome
local lowestValue = 1
local selectedBiome
for biome, value in pairs(valueDiffs) do
if value < lowestValue then
lowestValue = value
selectedBiome = biome
end
end
-- Generate the block
local block = Instance.new("Part")
block.Anchored = true
block.Size = Vector3.new(8, 8, 8)
block.Position = Vector3.new(x * 8, 0, z * 8)
block.Parent = game.Workspace.World
if grid[x][z]["Height"] <= 0.2 then -- Water level
block.BrickColor = BrickColor.new("Electric blue")
elseif selectedBiome == "Grasslands" then
block.BrickColor = BrickColor.new("Bright green")
elseif selectedBiome == "Taiga" then
block.BrickColor = BrickColor.new("Baby blue")
elseif selectedBiome == "Desert" then
block.BrickColor = BrickColor.new("Cool yellow")
elseif selectedBiome == "Swamp" then
block.BrickColor = BrickColor.new("Slime green")
elseif selectedBiome == "Mountains" then
block.BrickColor = BrickColor.new("Dark stone grey")
elseif selectedBiome == "Jungle" then
block.BrickColor = BrickColor.new("Earth green")
elseif selectedBiome == "Snowy tundra" then
block.BrickColor = BrickColor.new("White")
else
block.BrickColor = BrickColor.new("Bright green")
end
block.Position = Vector3.new(x * 8, 4.5, z * 8)
block.Parent = game.Workspace.World
end
game:GetService("RunService").Heartbeat:Wait()
end
Note: I’m aware that I can replace the many elseif statements, checking for the selected biome, to make it more efficient, please ignore that.
The “BiomeInfo” module script:
return {
["Grasslands"] = {
["Height"] = 0.27,
["Moisture"] = 0.21,
["Heat"] = 0.25
},
["Desert"] = {
["Height"] = 0.15,
["Moisture"] = 0.05,
["Heat"] = 0.49
},
["Taiga"] = {
["Height"] = 0.28,
["Moisture"] = 0.16,
["Heat"] = 0.12
},
["Swamp"] = {
["Height"] = 0.16,
["Moisture"] = 0.44,
["Heat"] = 0.14
},
["Mountains"] = {
["Height"] = 0.43,
["Moisture"] = 0.08,
["Heat"] = 0.11
},
["Jungle"] = {
["Height"] = 0.22,
["Moisture"] = 0.51,
["Heat"] = 0.31
},
["Snowy tundra"] = {
["Height"] = 0.17,
["Moisture"] = 0.12,
["Heat"] = 0.03
},
}