Random terrain

This article uses the term "terrain" to mean a landscape and uses Parts to construct the terrain, not the Terrain object.


Procedurally generated terrain, often referred to as randomly generated terrain, is a method of creating terrain by following a pattern or algorithm instead of hand-picking every single point. By using randomly generated terrain large maps can be made in a fraction of the time.

Contents

[edit] Basic algorithm

The most basic algorithm for terrain generation is to place a brick of a random size in a random position across the map. To do this we are going to use a for loop and create a new part for every iteration.

for i = 1, 200 do
    local part = Instance.new("Part", workspace) -- Creates a Part and makes it a descendant of Workspace
    part.Anchored = true -- Prevents the part from moving.

Then set the size to a random amount using math.random()

part.Size = Vector3.new(math.random(20), math.random(20), math.random(20))

The position will be done the same as the size was for the X and Z axis however, the Y axis will be treated differently. Since we want all the bricks to stay on the ground, the base of the block needs to be at 0. Because the Position of a brick in roblox uses the center of that brick, dividing the height of the brick by 2 makes it sit on 0.

part.CFrame = CFrame.new(math.random(-100, 100), part.Size.Y/2, math.random(-100, 100))
end -- for the for loop
The basic algorithm

[edit] Height-map

A height-map shows different elevations across a map. Height-maps are the most common form of terrain used. For this example, put a script in the base of the map. This way it is easy to access the size and position of the map. The first thing to do is define the variables needed.

local size = Vector3.new(10, -- Number of tiles along the width
                         10, -- Number of steps of heightmap
                         10) -- Number of tiles along the length
 
local base  = script.Parent -- The part to replace with terrain
 
local tilePart = Instance.new("Part")
      tilePart.Anchored = true
      tilePart.formFactor = "Custom"
      tilePart.Size = base.Size / size --The size of one unit or cuboid of the map

Sizing and positioning the bricks will be done similar to how it was in the first terrain generator except the only random factor will be the height of the brick.

for x = 1, size.x do
    for z = 1, size.z do
        local y = math.random(size.y)
        local tile = tilePart:clone()
 
        local position = Vector3.new(x-1, 0, z-1) * tile.Size
        tile.Size = tile.Size * Vector3.new(1, y, 1)
        tile.CFrame = CFrame.new(tile.Size/2)     --Shift the part by half it's size, so we can position the corner
        tile.CFrame = tile.CFrame - base.Size / 2 --Shift it into one corner of the base
        tile.CFrame = tile.CFrame + position      --Put it in the right place
        tile.CFrame = base.CFrame * tile.CFrame   --Move it so that it is level with the surface of the base
        tile.Parent = workspace
    end
end -- an end for each for loop
TerrainH.PNG

[edit] Coloring the bricks

Currently the bricks are very plain and dull. To liven things up, it is best to add some color to your terrain.

[edit] Random

So that there is some control over what colors are going into our terrain a table full of colors is going to be added. Then our function will pick a random color out of that table and designate that as the color of the brick.

local colors = {BrickColor.Red(), BrickColor.Black(), BrickColor.White()}
function color(part) 
    part.BrickColor = colors[math.random(#colors)]
end

Then add the line color(a) somewhere in the script and presto! Your terrain is now prettier.

Colorful.PNG

[edit] Based on height

Using the height of the brick is a common method used because it allows for you to make sandy beaches and snowy mountain-tops without causing too much costly checks. To do this, check the height of the bricks against the minimum height for each color.

local colors = {BrickColor.Red(), BrickColor.Black(), BrickColor.White()}
function color(part)
    if part.Size.Y > 7 then
        part.BrickColor = colors[3]
    elseif part.Size.Y > 3 then
        part.BrickColor = colors[2]
    else 
        part.BrickColor = colors[1]
    end
end

If you know enough about generic for loops then it is easier to speed up this process and also add more colors by using the index of the color as the minimum height.

function color(part)
    local colors = {
        [0]=BrickColor.Blue(),
        [3]=BrickColor.Yellow(),
        [8]=BrickColor.Green(),
        [17]=BrickColor.new("Reddish brown"),
        [24]=BrickColor.DarkGray(),
        [32]=BrickColor.White()
    }
 
    for minimum_height, color in ipairs(colors) do
        if part.Size.Y > minimum_height then
            part.BrickColor = color
        end
    end

[edit] Using weighted values

In scripting terminology, weighted values are values that have been tampered with to insure certain results.

[edit] Mountains

In case we wanted to make sure a mountain came out of our script, we can easily just use a variable ( rand ) to hold the highest point we reach and add that to every number. Now to maintain it being random, we will also add a random number between -2 and 2. Then after we have that number, we'll check if the random number between -2 and 2 is greater than 0 and add it to our variable "rand".

rand=0
 
for x=1,Xlength do
for z=1,Zlength do 
    local a=part:clone() 
    local random_factor=math.random(-2,2)
    tilePart.Size=Vector3.new(part.Size.X/Xlength,rand+random_factor,par.Size.Z/Zlength)
    if random_factor>0 then
        rand=rand+random_factor
    end
Mountain.PNG

[edit] Lake

Now to make a lake, we won't need random_factor or rand. All that will be needed is a little bit of math to add in the weighted factors. Just try

tilePart.Size=Vector3.new(par.Size.X/Xlength,math.random(-2,2)+math.abs(x-Xlength/2)+math.abs(z-Zlength),par.Size.Z/Zlength)

This works because it uses the distance from the middle as the weight.

Random lake.PNG

[edit] See Also