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.
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).
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?!
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.
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?
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!
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.
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
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.
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);
}
}
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?
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.