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 Loops#For 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