This tutorial will reference the word 'noise' a lot. Although for some it may seem obvious, the noise I'm referring to is a method of smoothly interpolating pseudo-random numbers.
What the heck does that mean? Well, interpolate means to smoothly transition. In the context of noise, this means we're going to get terrain that is "smooth", as opposed to random spikes all over the place.
Pseudo random means random enough to be called random (although if you knew the formula, you could predict what values could be returned, but that's out of the scope of this tutorial).
Noise in lua takes 3 parameters, an X, a Y, and a Z. The 'Z' argument is optional, although we'll most games use it as a 'seed' This will then return a smoothly interpolated value.
Now, roblox tells you noise returns a value between -.5 and .5, but they're lying. Sometimes Robloxscrews up, and gives a value above or below that. Why? I don't know. It's just important to remember that when coding.
If you're familiar with minecraft, then you'll know minecraft worlds are nearly infinite. How did the developers make infinite worlds? They sure as heck didn't build each one by hand, did they? Of course not. Instead, they used noise to generate height maps and biome maps to make seemingly random terrain with variation in every seed.
The basic idea to implement noise to generate terrain is this: we have a range of x and y positions, and we have a nested loop, (a loop inside another loop) that increments these x and y values by a certain offset value. Then we plug these x and y values into a noise function, and we set that value to the height of the heightmap at that x and y position.
Here's what that looks like in code: (just ignore the semi-colons; lua doesn't requre them but it's a habit of mine that I don't feel like breaking) (also, since there I can't copy and paste from an external text document into this custom text editor, there may be a few typos, please excuse them :) )
local seed = math.random(-200, 200);
local biomeIncrement = .07;
local heightScale = 10;
local width = 100;
local length = 100;
local partSize = 2;
local bottomColor = Color3.fromRGB(70, 62, 49);
local topColor = Color3.fromRGB(12, 95, 12);
local camera = game.Workspace.CurrentCamera;
camera.CFrame = CFrame.new(Vector3.new(-width * partSize/3, partSize * width/2, -length * partSize/3), Vector3.new());
camera.Focus = CFrame.new();
local partTemplate = Instance.new("Part");
partTemplate.Anchored = true;
partTemplate.CanCollide = false;
partTemplate.TopSurface = Enum.SurfaceType.Smooth;
local function clamp(val, min, max)
if val < min then
return min;
elseif val > max then
return max;
end
return val;
end
local function genTerrain()
local folder = game.Workspace:FindFirstChild("Folder");
if folder then
folder:Destroy();
end
folder = Instance.new("Model", game.Workspace);
folder.Name = "Folder"
local xOff = 0;
local yOff = 0;
for x = 1, width do
xOff = 0;
for y = 1, length do
local noiseValue = clamp(math.noise(xOff, yOff, seed);
local part = partTemplate:Clone();
part.Parent = folder;
part.CFrame = CFrame.new(x*partSize, (noiseValue * heightScale) - heightScale/2, y * partSize);
part.Name = noiseValue;
part.Color = bottomColor:Lerp(topColor, noiseValue + .5);
part.Size = Vector3.new(partSize, heightScale, partSize);
yOff = yOff + biomeIncrement;
end
xOff = xOff + biomeIncrement;
end
end
genTerrain();
The final product:
if the biome noise value is set to .5: