LibGdx - perline noise

Hi,

I’ve generated a map of 2000x1000 tiles (tile size 32x32). The map is scrollable in all directions (I only draw what the camera sees).

I want to use perlin noise to generate heights for my tiles, such as this ascii example:

G - grass, D - dirt, S - stone

G              GGG

GGG GGGGGG GG
GGGGGGGGGGGGGGGGGGG
GDDDGDDDDGGGGGGGDDG
DDDDDDDDDDDDDDDDDDDD
DDDDDDDDDDDDDDDDDDDD
DSDDDDDDDSDDDDDDSSDD
SSSSSSSSSSSSSSSSSSSSSS
SSSSSSSSSSSSSSSSSSSSSS

How do you apply perlin noise to achieve such a thing like the above?

Any advice is appreciated,

Thanks

http://www.redblobgames.com/articles/noise/introduction.html

This page gives a detailed explanation of noise functions and generating maps in general. It’s a great place to get you started.

Hey thanks for that, I did read that before.

I do have a SimplexNoise class I got from here in a thread, just don’t know how to use it.

Think you do:


SimpleNoise sn = new SimplexNoise();
float noise = sn.noise(xin, yin);     // not sure what these are - possibly the location of x,y tile in array?

I guess the noise is the height?

In my code I have a 2D array of LandscapeEntities which can be Rocks, Grass, Granite etc


	private LandscapeEntity[][] worldMap;

// Map is drawn each frame, only draws what camera sees

public void drawMap(boolean bDebug, SpriteBatch batch,
			TextureRegion[][] region, int sx, int sy, int ex, int ey, int w,
			int h) {
                             for (int row = sy; row < sy + ey + 1; row++)
				for (int col = sx; col < ex + 1; col++)
					worldMap[col][row].draw(batch, region);
}

// Fill map with default DirtEntity (this is derived from LandscapeEntity - simple polymorphism)
private void generateMap() {
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				addEntity(new DirtEntity(x, y)); // all just dirt
			}
		}
	}

	public void addEntity(LandscapeEntity entity) {
		worldMap[entity.x][entity.y] = entity;
	}


My problem is filling the map with my tiles, thus grass on top, dirt, rock etc as shown in the ascii image.
Map is just filled with dirt entities at the moment.

Thanks,
Steve

Check the tile height and see if it is in a certain range, narrow down what tiles can live in that range, and then choose a random one with weighted probability (essentially what minecraft does).

Thanks,

So from the value back from the noise function, say if in range of 0-20 draw lava, 20-30 granite, 30-50 rock, 50-80 dirt, 80-100 grass?

So say the noise function came back with 5, I’d draw lava 5 high, if so, what would you put in the empty space (6-20?, just leave as dirt?), am I getting confused here?!

Thanks again,
Steve

First off, there is a good reference to check out here at JGO:

(1) The inputs have to be scaled. Perlin Noise is set up so that there is grid of points at which there are gradients derived from a random function. The grid points are about 1 apart (or one “simplex” apart if you are using SimplexNoise). So you will have to divide your tile’s (x, y) by some factor so that your variables will follow the contours. You might experiment with divisors that are powers of 2, e.g., 1/16th to 1/128th, to see how much contouring you get. The smaller the factor, the more gradual the slopes.

    float noise = sn.noise(xin * k, yin * k);

I put the same k in both, but they can be scaled differently if it suits your geography. The k value can even be varied as a function of xin or yin if you wish to create something like a perspective effect with the noise values.

(2) The output will be a number from -1 to 1. You can set up how that is translated however you want. Sometimes folks first apply an ABS function, and then translate the numbers 0 to 1 to whatever type of environment. This will create a sort of “fold” in the output data. Using the ABS function is sometimes referred to as “turbulent” noise as opposed to using the data straight, which is called “smooth” noise.

(3) To get more dramatic results, people sometimes use multiple calls to Perlin noise, each with different k values, and add them up. The most common strategy is as a fractal function. This gets progressively more expensive, but can make much more interesting patterns.

Ken Perlin himself describes this method in a slide presentation, in slide 21 from a talk he gave. http://www.noisemachine.com/talk1/21.html

I have a post where this can also be seen, and a Java tool for experimenting with visualizations with Perlin Noise.
http://www.java-gaming.org/topics/simplex-noise-experiments-towards-procedural-generation/27163/msg/242735/view.html In the first post you can see four fractal calls to Perlin’s SimplexNoise function, with k values of 1/256, 1/128, 1/64, 1/32, that are progressively summed together, and the cloud pattern it produces. The tool lets you plug in different values and mix the different channels at different amounts. The instructions are sparse, but the code is available via github github.com/philfrei/SiVi, and there is a “gallery” of effects and some commented sample code exposed in the jar to look at as well.

I set up the tool so that the noise outputs point into a Color Map. But instead of a ColorMap with 256 divisions, you can certainly have a map with fewer divisions where each is a terrain type.

The values returned by the function will be -1 to 1, so you might first add 1 then multiply by 50 if you really want 100 gradations to assign to terrain types. If there are only 5 types, that might be overkill, unless you want to jigger the odds so some are more likely than others.

Can also do something like this:
if < -0.8 then granite
if < -0.6 then earth,
etc.

The values will fluctuate, so if the first is 5, the next might be 16 the next might be 9, the next might be 18, etc. etc. You will choose the terrain type that matches each number and have them contiguous on the screen.

Many thanks for taking the time and helping on this, very much appreciated.

I was thinking that grass would be in the 1st and 2nd row of the 2d array, dirt in the 3rd,4th,5th,6th,7th,8th say,
rock in the 9th,10th,11th,12th,13th,14th, granite in 15th,16th,17th,18th,19th,20th and then lava in the 20th-last possible row for instance, does
this sound correct?

Still unsure how this array would be filled in though, how do you generate a height for them?

Thanks

Doh! I should have read your post more closely. [I think] You are using 1 dimensional noise, not 2.

So, use the same Y value throughout, and just vary the X. The resulting number tells you how much to draw.

;D

So the value back from noise will state how many to draw vertically (height wise)?

I did use the 2d perlin noise way back for a voxel engine I wrote, wish I hadn’t been away from java gaming now for such a long time!

Was I correct in saying about where the grass, dirt etc would be in regards to the array or am I getting completely lost?!

Here is a link to website/blog of what I’m doing, you will see a screen shot of what I’m trying to achieve:

https://sites.google.com/site/sterrialand/development

Thanks as always

I did some basic terrain generation a while ago using OpenSimplex noise. It turned out pretty good:

The steps went a bit like this:


float hillCoeff = 8f;
		float terrainLimit = hillCoeff + (sizey / 8);
		int dirtLimit = Math.round(hillCoeff + (sizey / 8));
		int caveStart = dirtLimit - (sizey / 32);
        
                // generate the hills
		for (int i = 0; i < sizex; i++) {
			float noise = (float) noiseGen.eval(i * 0.15f, -1 * 0.15f);
			int actualHeight = Math.round(noise * hillCoeff);

			int topOfDirt = actualHeight + (sizey / 16);
			int endOfDirt = actualHeight + (sizey / 8);
			for (int y = endOfDirt; y >= topOfDirt; y--) {
				setBlock(Blocks.instance().getBlock("dirt"), i, y);
			}
			setBlock(Blocks.instance().getBlock("grass"), i, actualHeight + (sizey / 16));

			for (int y = endOfDirt; y < sizey; y++) {
				setBlock(Blocks.instance().getBlock("stone"), i, y);
			}

			// long cave under surface to cover up noise's abrupt end
			for (int y = caveStart - Math.round(4 * noise) - 6; y < caveStart
					+ Math.round(3 * noise) + 3; y++) {
				setBlock(Blocks.instance().getBlock("empty"), i, y);
			}
		}

		/* NOTES
		 * Terrain upper limit is essentially 1 * hillCoeff + (sizey / 8)
		 * dirt ends at around hillCoeff + (sizey / 8)
		 * noise ends at dirtLimit - (sizey / 32)
		 * caves start at dirtLimit - (sizey / 32)
		 * 
		 */
                
                // generate the caves
		float caveStartThreshold = 0.56f;
		float caveEndThreshold = 0.825f;
		for (int x = 0; x < sizex; x++) {
			for (int y = caveStart; y < sizey; y++) {
				float noise = (float) noiseGen.eval(x * 0.1f, y * 0.1f);
				noise = (noise + 1) / 2f;
				if (noise >= caveStartThreshold && noise <= caveEndThreshold) {
					setBlock(Blocks.instance().getBlock("empty"), x, y);
				}
			}
		}

Github code here.

The code generated the hills using 1D noise (that’s actually 2D, but the Y dimension is always -1), and added a cave at the bottom that’s a bit like the inverse of the hill heights. This is to cover up the cave generation stub.

Caves are generated with 2D noise and a min-max value. If the noise function returns a value (which is changed to 0-1 for ease) that is between the min and max it will set the block to air. Likewise, you could do the same thing except for ore deposits or different types of stone, etc. before cave generation.

I hope that helps! If you need me to explain more I can!

Hey many thanks for this, that does look very good, what is the size of the array you used?

Mine is currently [2000][1000].

Can be seen here:

https://sites.google.com/site/sterrialand/development/news

I’ve not put any height stuff in as of yet, I just have this:


private void generateMap() {
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				if(y >=0 && y<=100)
					addEntity(new LavaEntity(x, y),x,y); // lava
				if(y >100 && y<=200)
					addEntity(new CoalEntity(x, y),x,y); // coal
				if(y >200 && y<=750)
					addEntity(new RockEntity(x, y),x,y); // rock
				if(y >750 && y<=798)
					addEntity(new LandscapeEntity(x, y),x,y); // earth
				if(y >798 && y<=800)
					addEntity(new GrassEntity(x, y),x,y); 
				if(y >800 )
					addEntity(new BlankEntity(x, y),x,y);  // blank space
			}
		}
	}

Hence the reason it is all flat - need to add the noise to it.

Still unsure how you generate the hills with grass on top?

Thanks,
Steve

If you are using the same sequence of air/grass/dirt/rock/coal/lava in each column, and only the height varies, then yes.

If there are variations in the the thickness of the layers (implied by the graphic on your link), then you’ll need to do more. You could use a Perlin noise function to determine each of those thicknesses, again as a 1-dimensional value, then assemble the results into your array.

Thanks again,

I think there should be variations in thickness. Say I was doing the dirt, got height of 4 means do 4 dirt tiles on vertical, then next one said 3, what goes into the 4th space - see below:

GG
D <------ Do I put grass here?
DDD
DDD
DDD
R R <---- put dirt in the missing space here?
RRR
RRR
RRR

Thanks again

All my worlds have variable size (and are stored in 16x16 chunks when saved or transmitted from server to client) so there is no defined height or width. In the image I gave you the world happens to be 1024x512. The code is merely like a percentage of how much of what per column. Dirt and whatnot is generated from the BOTTOM of the dirt layer to the top, ending with a grass block at the top. The stone is generated from the bottom of the dirt layer to the very bottom of the world.

Also, having a separate object for every block in the world will quickly eat up memory. You should have one instance of a block that you will reuse over and over.

I would maybe use a mix of “air” and “lava”. Not sure how to make sure we don’t just end up with a flat middle layer (if we purely added in towards the center of the grid. I would have to think about that for a while.

e.g., something like the following

AAA
GAG
DGD
DDD
DDD
DDR
RRR
RRR
RRR
RLL

How you decide to fill in is up to you to decide. You might make up some rules based on how much slope is allowed for the Grass or another layer. Or, whatever helps the game-play the most or looks the best.

It’s not clear to me that Perlin Noise is the way to go for generating the randomness in this case. A simpler approach, such as having thickness variables for each layer that are allowed to wander randomly within a certain bounds (and sensitive to the location of the upper and lower neighbors) might be just fine.

You could do something along the lines of this:

HashMap<WeightedPossibility, Block> spawns = new HashMap<>();

public Block determineBlock(double y) {
	// Find all possible spawns
	ArrayList<WeightedPossibility> possible = new ArrayList<WeightedPossibility>();
	for (WeightedPossibility possibility : spawns.keySet()) {
		if (y >= possibility.min && y <= possibility.max) {
			possible.add(possibility);
		}
	}
	// Determine the block
	WeightedPossibility possibility = null;
	int index = 0;
	while (possibility == null && index < possible.size()) {
		if (Math.random() <= possible.get(index).chance) {
			possibility = possible.get(index);
		}
		index++;
	}
	return spawns.get(possibility);
}

public class WeightedPossibility implements Comparable<WeightedPossibility> {
	public double min, max, chance;
	public int compareTo(WeightedPossibility other) {
		return Float.compare(chance, other.chance);
	}
}

Many thanks,

Going to have a mess about to see what I can come up with using perlin noise, I believe this is what
games like terrarria use. Interesting what you do say though about using having thickness variables.

CopyableCougar4 - that looks very interesting, although how would you generate hills that are not too jagged?

chrislo27 - so you don’t use perlin noise, I was thinking on the lines you have said, but like above, how to you generate hills
that are not too jagged?

Many thanks all!

OpenSimplex noise is a type of simplex noise that doesn’t have patents on it. Simplex noise was made by the same guy who made Perlin noise (Ken Perlin), only difference is that simplex noise becomes much faster in more dimensions compared to Perlin noise.

If I wanted less jagged-y hills, I could tone down the amplitude (instead of multiplying each block coordinate by 0.15, it could be 0.05). The reason the hills are so jagged in my picture is because the blocks in my game are much bigger compared to Terraria.

Hi all,

I’ve added the grass level and dirt, please see my blog here:

https://sites.google.com/site/sterrialand/development/news

Using simplexnoise just to start the first grass block off, then some ordinary random stuff.

Just looking at now how to add caves to the map…suggestions?!

Thanks,
Steve