0

This is my first post in stack overflow, and I have no clue whether I can post stuff about roblox development here.

But anyways, here I go. I'm making a top-down pixel art survival game, and am procedurally generating "flat" worlds with many different biomes. The code is working fine, it's just that the results look really unnatural, and I don't know what to do about it.

Some examples of what I mean: world 1, world 2, world 3

Here is 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 the many elseif statements is inefficient, this is temporary, please ignore it.

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
    },
}

This is my first time working with procedural generation, so I'm not sure if I'm applying the most efficient methods for everything.

  • Perhaps try limiting the number of biomes. You wouldn't see a snowy tundra next to a swamp next to a desert. Try limiting to just a few at a time. Generate mountains with tundra, grasslands, and water. Or swamp with jungle and water. Fewer biomes may allow for better shapes for the ones that are generated. – Kylaaa Oct 11 '21 at 17:01
  • Please use this: https://codereview.stackexchange.com/ – m4n0 Oct 14 '21 at 01:37

0 Answers0