Written by Natesh Narain
on 

Procedural 2D Maps with Lua scripts

Recently adding Lua scripting to my mapgen tool. Which allows the generation logic to be moved outside of the main program.

In this project I am using LuaJIT and luabind.

Here’s what a map generator script might look like:

LAYERS = "Land Elevation Moisture Temperature Biomes Final"

LAND        = 0
ELEVATION   = 1
MOISTURE    = 2
TEMPERATURE = 3
BIOMES      = 4
FINAL       = 5


Tundra           = 0
BorealForest     = 1
BorealBarren     = 2
TemperatelForest = 3
Savanna          = 4
GrassLand        = 5
Desert           = 6
Rainforest       = 7

function generate(layers, params)
    landMaskNoise = params:getNoise("land_mask")
    elevationNoise = params:getNoise("elevation")
    moistureNoise = params:getNoise("moisture")
    temperatureNoise = params:getNoise("temperature")

    seaLevel = params:getFloat("sea_level")

    landDark = params:getColor("land_dark")
    landLight = params:getColor("land_light")
    waterLight = params:getColor("water_light")
    waterDark = params:getColor("water_dark")
    cold = params:getColor("cold")
    hot = params:getColor("hot")

    t1 = params:getFloat("t1")
    t2 = params:getFloat("t2")
    t3 = params:getFloat("t3")

    m1 = params:getFloat("m1")
    m2 = params:getFloat("m2")
    m3 = params:getFloat("m3")

    for x=layers:startX(), layers:endX()-1, 1 do
        for y=layers:startY(), layers:endY()-1, 1 do
            landMask = landMaskNoise:sample(x, y)

            cl = nil
            ce = nil
            cm = nil
            ct = nil
            cb = nil
            cf = nil

            if landMask > seaLevel then
                cl = Color.from(landDark, landLight, landMask)

                elevation = utils.range(elevationNoise:sample(x, y), -1, 1, 0, 1)
                ce = Color.from(Color(0), Color(1), elevation)

                moisture = utils.range(moistureNoise:sample(x, y), -1, 1, 0, 1)
                cm = Color.from(waterLight, waterDark, moisture)

                temperature = utils.range(temperatureNoise:sample(x, y), -1, 1, 0, 1) * 2.0
                ct = Color.from(cold, hot, temperature / 2.0)

                biome = getBiome(temperature, moisture, t1, t2, t3, m1, m2, m3)
                cb = biomeToColor(biome, params)

                cf = Color.from(cb, landDark, elevation)
            else
                cl = Color.from(waterLight, waterDark, 1.0 - landMask)
                ce = Color(1)
                cm = Color(1)
                ct = Color(1)
                cb = Color(1)

                cf = cl
            end

            layers:set(LAND, x, y, cl)
            layers:set(ELEVATION, x, y, ce)
            layers:set(MOISTURE, x, y, cm)
            layers:set(TEMPERATURE, x, y, ct)
            layers:set(BIOMES, x, y, cb)
            layers:set(FINAL, x, y, cf)
        end
    end
end

function getBiome(t, m, t1, t2, t3, m1, m2, m3)
    if t >= t1 then
        if m >= m1 then
            return Rainforest
        elseif m >= m2 then
            return TemperatelForest
        elseif m >= m3 then
            return GrassLand
        else
            return Desert
        end
    elseif t >= t2 then
        if m >= m1 then
            return TemperatelForest
        elseif m >= m2 then
            return GrassLand
        elseif m >= m3 then
            return GrassLand
        else
            return Desert
        end
    elseif t >= t3 then
        if m >= m2 then
            return BorealForest
        else
            return BorealBarren
        end
    else
        return Tundra
    end
end

function biomeToColor(biome, params)
    if biome == Rainforest then
        return params:getColor("rainforest")
    elseif biome == TemperatelForest then
        return params:getColor("temperate")
    elseif biome == GrassLand then
        return params:getColor("grassland")
    elseif biome == Desert then
        return params:getColor("desert")
    elseif biome == BorealForest then
        return params:getColor("boreal")
    elseif biome == BorealBarren then
        return params:getColor("boreal_barren")
    else
        return params:getColor("tundra")
    end
end

Image not found!

Though at the moment this is poorly optimized. Every loop new Color objects are dynamically allocated and this is slowing the generator down considerably.

Connected Posts

Previous in Project
Procedural 2D Maps
Next in Project
Procedural World - Attempts at Terrain
Top