Random Map Generation

Hello there, I’m new here, and thought I’d share something isntead of only being here to ask annoying questions.

I’m working on some random map generation for a game I am working on, and I was thinking that some sharing of thoughs on map generation would be nice, since my stuff so far is far from perfect.

What I am doing at the moment, is rather simple, and would be best described in a snippet of code:

//Repeat for a set number of times
for(int i=0;i<laketimes;i++)
    {
      //Pick a point somewhere in the map
      pointX=genValue(WIDTH);
      pointY=genValue(HEIGHT);
      for(int x=0;x<WIDTH;x+=1)
      {
        for(int y=0;y<HEIGHT;y+=1)
        {
          //For every value, take the distance
          int t=distance(x,y,pointX,pointY);
          //And then add to the map's height, a number depending on Cos(Distance*90)
          value[x][y]+=(int)(100.0*Math.cos(t/10+90));
        }
      }  
    }

I also added in a bit of white noise over the whole thing, and afterwards I smooth it out, by for every x,y assigning the average of the surrounding values. I do this two times but I guess that is all down to flavor, here’s a pic of my method:

http://img16.imageshack.us/img16/1082/randommap.png

As you can see, everything seems to be a bit circling arround the lakes.

So anyone have past experience with generating maps?

If you use regular functions like cos then you’re going to get regular results…
A simple solution would be to add multiple cos functions at different resolutions.Try;

value[x][y]+=(int)(100.0*Math.cos(t/10+90)+50*Math.cos(t/5+45));

for example.

A more advanced solution would be Perlin Noise.

You mean like IRL? >_> I think that map looks awesome…

I wish Hugo would edit this page. This is “value” noise and not Perlin. The concepts are fine, the noise function is not. There are various noise implementations in various thread here which can be searched for.

Nice. How many tiles across is that?

This is very interesting and cool. Hopefully one day I can do stuff like this.

~Shazer2

I see how you are storing the values into the 2D array of “values” but what exactly do you do with those values? How do you render the map?

Anyway, the map looks really nice, good work and thanks!

I really like that map - it has a “real” look to it.

Cas :slight_smile:

Thanks for that tip, I was actually aiming for Perlin noise, but my math skills are pretty meh, so I just left it at cos(blah), but that link is really helpfull. I got the idea of taking points and aplying a “formula” from the distance from this link: http://www.noisemachine.com/talk1/, it’s from one of the guys who did 3d graphics for the Tron movie, apparently all of that stuff was just algorithms, no prefabs. It’s quite an interesting read, if anyone’s interested.

It’s 200x200, I’m using a tilesize of 8x4, but the tiles I’m drawing are atcually 16x8 :P, so if you zoomed in, it would look awkard. Just doing it for testing sake, the real tiles are 64x32.

It’s nothing very fancy, I just have a 2D array storing what tile to draw at a position, and one of my friends made me a nice tileset. What part of this looks difficult? I might be able to give you a pointer in the right direction.

I have a set of tiles stored in an array “tilesheet[]” of which 1 is Water, 2 is Sand, 3 is Grass, 4 is elevated Grass, 5 is more elevated grass and 6 is most elevated grass. To determine the tile for every tile I use the value[][] that I calculate with my mapgenerating algorithm, the script looks like this:

  public int getTile(int x, int y)
  {
    int ret=value[x][y];
    if(ret<35) return 1;
    if(ret<45) return 2;
    if(ret<75) return 3;
    if(ret<90) return 4;
    if(ret<95) return 5;
    return 6;
}

I just fiddled with these numbers untill it looked nice, I’m not a very organized programmer, I ussually just mess arround untill it looks good/works.

Thanks for all the positive feedback (: does anyone have ideas on how you could create a method that also works with an expandable world? The weakness of my method is that, if you want the expand the world, the “seeds” that you used will all be really close, while the rest of the world is far away, resulting in simply circles arround your original part of the map:

http://img23.imageshack.us/img23/1074/randmapodd.png

The north quarter of the map was the original map in this image.

You draw every tile, one at a time? and you just draw textured cubes?
like
for (int x = 0; x < numTilesX; x++)
{
for (int z = 0; z < numTilesZ; z++)
{
//draw the highest(y value) block here and ignore the lower ones?
}
}

Edit: this is 2d?

It’s 2d, my tileset kind of has the height in it, like this:

http://img651.imageshack.us/img651/9607/tilesetb.png

Then I just draw them one by one, starting from the top to make sure the front ones are in front of the back ones.

[quote=“Pinguinpanic,post:9,topic:37886”]
Forgot to say, you might find this useful.

Right, here the circle look is very visible indeed and it looks less good. Other than that, nice looking rendering! :slight_smile:

Mike

The Cos function is a cyclic function, meaning - statistically - it will bear similar results over time. Maybe your circling behavior can be accounted to the use of the Cos function?

Try spicing it up, either by adding another Cos function with a different argument, or modifying the argument of the Cos function by a random multiplier. Not sure if these would work, but they are at least worth a try.

Then make the seeds not so close to eachother… spread them evenly across you larger world.

It would probably a good idea too to fade the influence of a ‘seed’ with distance.

Problem is, if I add a piece of map, with a seed on it, it will have to have an effect on the previously generated map, unless I make it fade somehow at the edges. Maybe I could just choose my “seeds” somewhere outside of my current map I just realized, there’s no reason for the calculation to require the seeds to be within the array, so I could try that and see how it looks.

What you’re (in effect) attempting to do is fake noise, so unless you’re having fun, just change to using some noise function.

Here a simple mapgenerator, not fancy, but it will create the hightmap of some island type landscape

import java.applet.Applet;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.util.Random;

/*
simple Island generator by Damocles
*/

public class MapGen extends Applet implements Runnable
{

	static int wide = 1000;
	static int high = 800;

	public void start()
	{
		new Thread(this).start();
	}

	public void run()
	{
		while (!this.isActive())
			Thread.yield();
		
		setSize(wide,high);
		Random ran = new Random();

		BufferedImage image = new BufferedImage(wide, high, BufferedImage.TYPE_INT_RGB);
		
		int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

		double[][] map = new double[wide][high];

		
		int iterator=1;
		int stepsize=wide/16;
		do
		{
		for (int y = stepsize; y <= high-stepsize*2; y+=stepsize)
		{
			for (int x = stepsize; x <= wide-stepsize*2; x+=stepsize)
			{
				//apply random offset (tweak this)
				int val=ran.nextInt(40+iterator*2)-20 - iterator*1;
				
				//initial features
				int valFirst=50+ran.nextInt(200);
				
				for (int yy = 0; yy < stepsize; yy++)
				{
					for (int xx = 0; xx < stepsize; xx++)
					{
						if(iterator==1) 	map[x+xx][y+yy] =valFirst;
						else {
							
						//apply random value	
						map[x+xx][y+yy] += val; 
						
						//average out
						map[x+xx][y+yy]=	
							(map[x+xx][y+yy]*2 + 
							map[x+stepsize+xx][y+yy] +
							map[x-stepsize+xx][y+yy] +
							map[x+xx][y+yy+stepsize] +
							map[x+xx][y+yy-stepsize] +
							
							map[x+stepsize+xx][y+yy+stepsize] +
							map[x+stepsize+xx][y+yy-stepsize]+
							map[x-stepsize+xx][y+yy+stepsize] +
							map[x-stepsize+xx][y+yy-stepsize] 
							
							) / 10 ;
						}
						if(map[x+xx][y+yy]>255) map[x+xx][y+yy]=255;
						if(map[x+xx][y+yy]<0) map[x+xx][y+yy]=0;
						
					}
				}
				
				
			}
		}
		
		iterator++;
		stepsize/=2;
		
		} while(stepsize>0);
		
		
		//smoothing
		for(int passes=0;passes<10;passes++)
		{
			
		
		for (int y = 1; y < high-1; y++)
		{
			for (int x = 1; x < wide-1; x++)
			{
				
				map[x][y]=	
					(
					map[x][y]*2 + 
					map[x+1][y] +
					map[x-1][y] +
					map[x][y+1] +
					map[x][y-1] +
					
					map[x+1][y+1] +
					map[x+1][y-1]+
					map[x-1][y+1] +
					map[x-1][y-1] 
					
					) / 10;
			}
		}
		}
		
		
		
		for (int i = 0; i < pixels.length; i++)
		{
			//make water 
			pixels[i]=0x40A0FF;
			
			int mv= (int)map[i % wide][i / wide];
			int mvRB= (int)(map[i % wide][i / wide]*0.5);  //make Red and Blue darker
			if(mvRB>255) mvRB=255;
			
			if(mv>140)
				{
				pixels[i] = (mvRB << 16) | (mv <<8) | mvRB;
				if(mv<152)pixels[i] = 0xE0D090;  //beach
				}
			
		}

		while (isActive())
		{
			this.getGraphics().drawImage(image, 0, 0, null);
			Thread.yield();
		}
	}

	
}


There are many parameters that can be tweaked.
Especially how much noise is applies for each step, and how much it should be smoothed.

This is basically value noise broken into a two step (and significantly more expensive) processes…why bother? Just use an existing noise function rather than re-inventing the wheel.