Not so random level generator.

I am a big fan of procedural generated content, but what if we generated something of which the outcome was exactly what you suspected it to be instead of a random coherent mess? I thought about this subject a lot, and I found quite a convincing solution.

As a programmer, amature musician, and math enthousiast, I keep finding myself using sine. And with the help of Boomlinde, a good friend of mine, I learned how to create a sine with a static pulse width. Which makes it loopable.

The formula for this is:

sin(n * 2 * pi / w).

Where w is the width.

Now, if you create a simple function based on several of those formulas with different pulsewidths, (for instance by adding them together) you get a loopable coherent terrain contour.

For convenience, I will add the class below.

Enjoy!

Download .love file

The textures are by Surt and can be found here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
--Screen
Screen_Width = 800
Screen_Height = 600
 
--Textures
local GrassTexture;
local DirtTexture;
local TileSize = 16;
 
-- Chunk Generation Stuff
local ChunkWidth = 128;
local ChunkHeight = 8;
local HeightTable = {};
local Flatness = 4;
local LevelBase = 4;
 
--Actual Level
local LevelHeight = math.floor(Screen_Height / TileSize);
local LevelWidth = ChunkWidth;
local Level = {};
 
--Looping Method
local TimeLoop = 1;
local Speed = 60;
 
--Blocks
local Block_Air = 0;
local Block_Dirt = 1;
local Block_Grass = 2;
local Block_Flower = 3;
 
function love.load()
	love.graphics.setBackgroundColor(132,202,255,255); -- Nice cyan hue, for an easy and convincing air.
 
	--Load the textures
	GrassTexture = love.graphics.newImage("IMG/Grass.png");
	DirtTexture = love.graphics.newImage("IMG/Dirt.png");
	FlowerTexture = love.graphics.newImage("IMG/Flower.png");
 
	for x = 1, LevelWidth do -- Make the Level table 2 dimensional.
		Level[x] = {};
	end
 
	GenerateHeight();
	HeightTo2dTable(HeightTable);
end
 
function GenerateHeight()
 
	for x = 1, LevelWidth do --for each x in the width of the level, do:
		Height = math.sin(x * 2 * math.pi / ChunkWidth); -- First offset, returns a sin with a length of ChunkWidth.
		Height = Height + math.sin(x * 2 * math.pi / (ChunkWidth / 2)); -- Second offset (additive), returns a sin with half the length of ChunkWidth.
		Height = Height * ChunkHeight / 2 + ChunkHeight -- Sin outputs a value between -1 and 1, so * ChunkHeight /2 + ChunkHeight / 2  == 0 to 16 (If Chunkheight = 16).
 
		HeightTable[x] = math.floor(Height) + LevelBase -- I do floor, because it's a simple way to get an integer instead of float.
	end -- end for each x in LevelWidth loop.
end
 
function HeightTo2dTable(HeightTable)
	for x = 1, LevelWidth do --for each x in the width of the level, do:
		for y = 1, LevelHeight do --for each y in the height of the level, do:
				Level[x][y] = Block_Air;
			if y > LevelHeight - HeightTable[x] then
				Level[x][y] = Block_Dirt;
			elseif y == LevelHeight - HeightTable[x] then
				Level[x][y] = Block_Grass;
				if math.random(25) == 1 then
					Level[x][y - 1] = Block_Flower;
				end -- end Random
			end -- end if statement
		end -- end for y loop
	end -- end for x loop
end -- end function
 
function love.update(dt)
	TimeLoop = TimeLoop + Speed * dt -- adds speed to the timer
end
 
function love.draw()
 
	-- This value will offset the level, based on the TimeLoop timer.
	DrawOffset1 = TimeLoop % (LevelWidth * TileSize)
	DrawOffset2 = TimeLoop % (LevelWidth * TileSize) - (LevelWidth * TileSize)
 
	for x = 1, LevelWidth do --for each x in the width of the level, do:
		for y = 1, LevelHeight do --for each y in the height of the level, do:	
			if Level[x][y] == Block_Dirt then
				love.graphics.draw(DirtTexture, x * TileSize - DrawOffset1, y * TileSize)
				love.graphics.draw(DirtTexture, x * TileSize - DrawOffset2, y * TileSize)
			elseif Level[x][y] == Block_Grass then
				love.graphics.draw(GrassTexture, x * TileSize - DrawOffset1, y * TileSize)
				love.graphics.draw(GrassTexture, x * TileSize - DrawOffset2, y * TileSize)
			elseif Level[x][y] == Block_Flower then
				love.graphics.draw(FlowerTexture, x * TileSize - DrawOffset1, y * TileSize)
				love.graphics.draw(FlowerTexture, x * TileSize - DrawOffset2, y * TileSize)
			end -- end if statement
		end -- end for y loop
	end -- end for x loop
end -- end function

Leave a Reply