Java-Gaming.org Hi !
Featured games (91)
games approved by the League of Dukes
Games in Showcase (804)
Games in Android Showcase (237)
games submitted by our members
Games in WIP (867)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  LibGdx - perline noise  (Read 8051 times)
0 Members and 1 Guest are viewing this topic.
Offline steg90

Senior Devvie


Medals: 2



« Posted 2015-05-20 18:24:28 »

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
Offline PocketCrafter7

Senior Devvie


Medals: 6
Projects: 2
Exp: 2 years


One man's bug is another man's feature


« Reply #1 - Posted 2015-05-20 18:58:07 »

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.

Nothing is difficult in this world. It is just how you look at it.
Offline steg90

Senior Devvie


Medals: 2



« Reply #2 - Posted 2015-05-20 19:08:03 »

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:

1  
2  
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

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
   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
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline CopyableCougar4
« Reply #3 - Posted 2015-05-20 19:25:42 »

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).

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline steg90

Senior Devvie


Medals: 2



« Reply #4 - Posted 2015-05-20 21:01:38 »

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
Offline philfrei
« Reply #5 - Posted 2015-05-20 21:22:39 »

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:

1  
2  
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?


First off, there is a good reference to check out here at JGO:
http://www.java-gaming.org/topics/noise-bandpassed-white/27071/view.html

(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.

1  
    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.

music and music apps: http://adonax.com
Offline philfrei
« Reply #6 - Posted 2015-05-20 21:30:02 »

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


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.

music and music apps: http://adonax.com
Offline steg90

Senior Devvie


Medals: 2



« Reply #7 - Posted 2015-05-20 21:39:30 »

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
Offline philfrei
« Reply #8 - Posted 2015-05-20 22:34:01 »

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.

music and music apps: http://adonax.com
Offline steg90

Senior Devvie


Medals: 2



« Reply #9 - Posted 2015-05-21 08:41:48 »

 Grin

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

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline chrislo27
« Reply #10 - Posted 2015-05-21 14:49:39 »

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


The steps went a bit like this:
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  
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!
Offline steg90

Senior Devvie


Medals: 2



« Reply #11 - Posted 2015-05-21 15:23:28 »

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:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
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
Offline philfrei
« Reply #12 - Posted 2015-05-21 17:44:36 »

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

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.

music and music apps: http://adonax.com
Offline steg90

Senior Devvie


Medals: 2



« Reply #13 - Posted 2015-05-21 20:45:41 »

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
Offline chrislo27
« Reply #14 - Posted 2015-05-21 22:23:03 »

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.
Offline philfrei
« Reply #15 - Posted 2015-05-21 23:26:43 »

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

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.

music and music apps: http://adonax.com
Offline CopyableCougar4
« Reply #16 - Posted 2015-05-21 23:31:33 »

You could do something along the lines of this:
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  
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);
   }
}

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline steg90

Senior Devvie


Medals: 2



« Reply #17 - Posted 2015-05-22 07:13:57 »

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!
Offline chrislo27
« Reply #18 - Posted 2015-05-24 16:22:26 »

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.
Offline steg90

Senior Devvie


Medals: 2



« Reply #19 - Posted 2015-05-26 13:19:35 »

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
Offline steg90

Senior Devvie


Medals: 2



« Reply #20 - Posted 2015-05-28 20:47:49 »

Hi,

If you have seen latest screens of my hills on the link in this thread you will see what my code is generating.

I only use perlin noise to get the start position of the first block on the Y-axis and then just allow the next block to be either above previous one by one bloack, same level or
below by one block, is this a good idea or should I just use the perlin noise to get a height factor and render that amount of blocks on the y-axis?

Reason I'm asking is I'd like to have big hills, and my code at the moment isn't really doing this.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
      int yOffset = 10; 
      addEntity(new GrassEntity(0, GRASS_START + yOffset));
      for (int x = 1; x < w; x++) {
         int pos = r.nextInt(3);
         if (pos == 1 && yOffset < GRASS_MAX - GRASS_START)
            yOffset += 1;
         else if (pos >= 2 && yOffset >= 0)
            yOffset -= 1;
         addEntity(new GrassEntity(x, GRASS_START + yOffset));
      }


Also, is using cellular automata technique be the way to go to generate some caves?

Thanks,
Steve
Pages: [1]
  ignore  |  Print  
 
 

 
Riven (397 views)
2019-09-04 15:33:17

hadezbladez (5280 views)
2018-11-16 13:46:03

hadezbladez (2204 views)
2018-11-16 13:41:33

hadezbladez (5544 views)
2018-11-16 13:35:35

hadezbladez (1150 views)
2018-11-16 13:32:03

EgonOlsen (4585 views)
2018-06-10 19:43:48

EgonOlsen (5462 views)
2018-06-10 19:43:44

EgonOlsen (3119 views)
2018-06-10 19:43:20

DesertCoockie (4016 views)
2018-05-13 18:23:11

nelsongames (4708 views)
2018-04-24 18:15:36
A NON-ideal modular configuration for Eclipse with JavaFX
by philfrei
2019-12-19 19:35:12

Java Gaming Resources
by philfrei
2019-05-14 16:15:13

Deployment and Packaging
by philfrei
2019-05-08 15:15:36

Deployment and Packaging
by philfrei
2019-05-08 15:13:34

Deployment and Packaging
by philfrei
2019-02-17 20:25:53

Deployment and Packaging
by mudlee
2018-08-22 18:09:50

Java Gaming Resources
by gouessej
2018-08-22 08:19:41

Deployment and Packaging
by gouessej
2018-08-22 08:04:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!