I'm working on a framework for city-builders (similar in appearance to SimCity2000, Tropico, Populous). I'm not actually making a game, it's more just to learn some techniques. I'll probably release it as a library if I get anywhere with it.
Has anyone here ever worked on anything similar who could give any nifty tricks for generating landscapes?
At the moment I'm using a matrix of 'elevation values' to dictate the heights of the vertices where turfs corners meet. I'm essentially just placing circles of elevation=1 at random within a bounding box of that matrix. Which seems like a pretty brain-dead way of doing it. What better alternatives are there?
Then I'm transferring that matrix to turfs to render them in pseudo-3D using isometric sprites and pixel-offsets). Here's how it looks so far: http://filesmelt.com/dl/island6.png
Then there's the issue of mountains. I was thinking of placing a few random 'peaks' and then using a blur algorithm on my matrix to smooth it out into mountains and hills and stuff. I'd then pass back over and add a few random lumps and bumps. What alternatives are there? Drawing concentric circles adding 1 to the elevation value each time?
The grand plan is to have a semi-realistic weather system. Wind will change direction over time creating a vector-field over the map. Clouds will pass overhead, pick up moisture from the sea and release it according to the elevation of turfs below it. This would all influence fertility of farmland, soil-erosion, flooding etc.
So, lots of questions. :S
Any links to examples/other-games/guides that could help would be very much appreciated. Even if it's games that look similar that I could go to for inspiration.
Thanks for your time.
Here's what the bulk of the code looks like so far, it's yet to be optimised or anything.
MapGen Code:
var/datum/map_generator/mapgen = new /datum/map_generator()
/datum/map_generator
var/list/matrix
var/max_elevation = 10
var/water
var/water_max = 10
var/island_size = 30
var/min_radius = 2
var/max_radius = 5
var/min_edge
var/max_edge
proc/make_new_island(placement, seed, new_island_size, new_water_max)
set background = 1
//Using a pre-defined PRNG seed
if(seed) rand_seed(seed)
//Initializing variables:
//Island
if(new_island_size > 30)
island_size = round(new_island_size)
var/island_area = island_size*island_size //Number of elements in the matrix
//Water
if(new_water_max > 0.1 && new_water_max < 0.9)
water_max = round(new_water_max * island_area)
water = island_area //Island starts as a sea
//Initializing the island's Matrix
matrix = new /list(island_size,island_size)
min_edge = max_radius + 1
max_edge = island_size - min_edge
switch(placement)
if(1)
place_lumps_on_square()
else
place_lumps_randomly()
proc/place_lumps_randomly()
var/iter = 1
while(1)
if(iter++ > 999) break
if(water_max > water) break
var/x = rand(min_edge,max_edge)
var/y = rand(min_edge,max_edge)
var/radius = rand(min_radius,max_radius)
place_lump(x,y,radius)
proc/place_lumps_on_square()
var/iter = 1
while(1)
if(iter++ > 999) break
if(water_max > water) break
var/center = round(island_size/2)
var/ring_radius = round(center/2)
var/hits = 0
for(var/i=-ring_radius,i<=ring_radius,i++)
for(var/j=-ring_radius,j<=ring_radius,j++)
matrix[center+i][center+j] = 1
if(abs(i) == ring_radius && abs(j) == ring_radius)
if((hits++)%8 == 0)
place_lump(center+j,center+i,max_radius)
proc/place_lump(x,y,radius)
if(matrix[y][x]!=null) return
matrix[y][x] = 0
for(var/dx=1,dx<=radius,dx++)
for(var/dy=1,dy<=radius,dy++)
var/dist = cheap_pyth_hyp(dx, dy)
if(dist <= radius)
matrix[y+dy][x+dx] = 1
matrix[y-dy][x+dx] = 1
matrix[y+dy][x-dx] = 1
matrix[y-dy][x-dx] = 1
matrix[y+dx][x] = 1
matrix[y-dx][x] = 1
matrix[y][x+dx] = 1
matrix[y][x-dx] = 1
proc/commit_map()
elevationmatrix = matrix
matrix2map()
Matrix2Map
proc/matrix2map()
var/i_max = length(elevationmatrix)
var/j_max = length(elevationmatrix[1])
world.maxx = j_max - 1
world.maxy = i_max - 1
world.maxz = 1
var/list/icon_states = icon_states('wireframe2.dmi',1)
for(var/x=1,x<=world.maxx,x++)
for(var/y=1,y<=world.maxy,y++)
var/turf/T = locate(x,y,1)
if(!T) continue
T.gradient[1] = elevationmatrix[x][y+1]
T.gradient[2] = elevationmatrix[x+1][y+1]
T.gradient[3] = elevationmatrix[x+1][y]
T.gradient[4] = elevationmatrix[x][y]
var/lowest = 50
for(var/grad in T.gradient)
if(grad <= lowest)
if(grad == null)
grad = 0
lowest = grad
T.elevation = lowest
var/new_icon_state = ""
for(var/val in T.gradient)
if(val > T.elevation)
new_icon_state += "1"
else
new_icon_state += "0"
if(!(new_icon_state in icon_states))
world.log << "x=[x], y=[y]"
new_icon_state = "0000"
if(T.elevation == 0 && new_icon_state == "0000")
T.icon_state = "water"
else
T.icon_state = new_icon_state
T.pixel_y = 32*T.elevation
T.pixel_x = -T.pixel_y
Math:
//A very crude linear approximatiaon of pythagoras theorem.
/proc/cheap_pyth_hyp(dx, dy)
dx = abs(dx)
dy = abs(dy)
if(dx>=dy) return dx + (0.5*dy) //The longest side add half the shortest side approximates the hypotenuse
else return dy + (0.5*dx)
//we take EAST as theta = 0 and rotate anti-clockwise
/proc/polar2cartesian(r, theta)
return list(r * cos(theta), r * sin(theta))
//rotate a matrix counter-clockwise by rotation lots of 90degrees
/proc/rotate_matrix(list/matrix, rotation)
rotation %= 4
if(rotation < 0) rotation += 4 //ugh! BYOND! returning a value outside of the ring of positive integers.
var/i_max = length(matrix)
var/j_max = length(matrix[1])
switch(rotation)
if(1) //anticlockwise
. = new /list(j_max++,i_max++)
for(var/i=1,i<i_max,i++)
for(var/j=1,j<j_max,j++)
.[j_max-j][i] = matrix[i][j]
if(3) //clockwise
. = new /list(j_max++,i_max++)
for(var/i=1,i<i_max,i++)
for(var/j=1,j<j_max,j++)
.[j][i_max-i] = matrix[i][j]
if(2) //180 degrees
. = new /list(i_max++,j_max++)
for(var/i=1,i<i_max,i++)
for(var/j=1,j<j_max,j++)
.[i_max-i][j_max-j] = matrix[i][j]
else
. = new /list(i_max++,j_max++)
for(var/i=1,i<i_max,i++)
for(var/j=1,j<j_max,j++)
.[i][j] = matrix[i][j]
//Transpose a matrix
/proc/transpose_matrix(list/matrix)
var/i_max = matrix.len
var/j_max = length(matrix[1])
. = new /list(j_max++,i_max++)
for(var/i=1,i<i_max,i++)
for(var/j=1,j<j_max,j++)
.[j][i] = matrix[i][j]
Perlin Noise is quite a good algorithm to generate random terrain with random elevation, while keeping it smooth at the same time. Here's article and Python source: http://breinygames.blogspot.com/2012/06/ generating-terrain-using-perlin-noise.html