Hacker News new | past | comments | ask | show | jobs | submit login
Building an Infinite Procedurally-Generated World (atomicobject.com)
72 points by philk10 on May 4, 2015 | hide | past | favorite | 39 comments



Perlin Noise is a cheap way to get repeatable and smooth randomness but should just be one tool in a world generation toolbox. I think most infinite worlds quite quickly collapse in 'interestingness' because after travelling for a short while in any direction you quickly see that there is nothing new out there.

So, instead of creating infinite worlds I'd like to see a lot more focus on procedurally-generating very interesting finite worlds. For games with a simple 2d tile-based grid, we then don't have to limit ourselves to just crafting noise-based functions -- as we can create the entire world at once. Games like Dwarf Fortress, Spelunky, Nethack, Terraria, etc., all follow this approach, and I'm trying to do the same in my own game, Moonman. So get our your IDEs and start creating house, face, forest, creature, item, and cloud generators. :)


I agree that using one technique for an infinite world is probably going to get boring pretty quickly, and you can probably get more leverage out of a finite world.

But even with an infinite landscape, I think you could improve the variety by using a generator at different scopes - if your landscape is just one constant type of terrain, like a field, things will get boring. You might have a wide variety of rivers and trees, but the basic idea is the same. But if you have another generator controlling the terrain type - going between plains, desert, mountains, etc., that would be really interesting. At the small scope, variation in trees and water is still the same level of change, but there is also a larger scope that changes the overall feel at a much larger radius.


Oh definitely. Minecraft does this in a simple but effective way. Though the interestingness of the landscape lasts until you've seen a few instances of each type, and then you have a model of the system in your mind, because it's just a few simple functions composed together. There is a lot of fun stuff to do so the terrain is only just the backdrop to that fun.

Instead of {noise(x,y)>0.5 = desert} let's simulate eons of weather, geological events, floods, and then feed that back into the lore of the game world. Dwarf Fortress is one of the few games I'm aware of that attempts this level of world generation. Unfortunately it's quite impenetrable as a game, due to its complexity.


The game FUEL is a great example. The game covers 14,400 km² but doesn't repeat itself.

http://en.m.wikipedia.org/wiki/Fuel_(video_game)


Heh. I agree, and I'm doing the same thing for my game.

As for Perlin noise, check out Simplex noise. It's what Perlin designed AFTER Perlin noise. I use 4D Simplex fractals to create my game world which tiles seamlessly when scrolling up/down as well as left/right. And I'm filling the world with random-but-connected cities, factions etc. That's where interesting emergent gameplay comes from.

BTW, I have a few writeups (quite technical) on the correct implementation of Simplex noise on spiralcode.wordpress.com


Nice! I really like the art style; but isn't >2D Simplex noise patented?


Really wish people using Oryx sprites would credit Oryx. And the ultimately irony is when people claim that the sprites are ripping off Realm Of The Mad God, which of course, itself, was initially based on... Oryx sprites.


I don't see any free or open source sprites. There is nothing on the website indicating that they are free or open source. Are you asking someone to credit the creator of sprites they paid money to use? That's absurd to me. It's like wanting this guy to credit Verizon or Time Warner for the bandwidth he paid for. If Oryx sprites were open source, or free for noncommercial use, I think it would be a legitimate claim. Otherwise, not so much, because it boils down to advertising. Crediting someone else is advertising someone else, and I believe giving someone actual money automatically means they don't need to also advertise for them.


The little man in the screenshot is from the "LOFI Fantasy 2D/3D" tileset[1], which has a CC BY-NC-ND license. After a quick look around, doesn't look like it's available on Oryx's site any more, however.

[1]: http://forums.tigsource.com/index.php?topic=8970.0


Unintentional, that's what happens when you write a toy not intending to publish it. Article has been updated with credit.


(thumbs up!)


How can we make realistic-looking continents? This project (and Minecraft, which looks similar, in virtue of using Perlin noise the same way) result in interesting and varied terrain for an RPG, but the terrain looks otherworldly or artificial if you zoom all the way out. The same variety that keeps the game from being boring when you're a human-sized observer makes it chaotic and noisy when you have a satellite's view.

When I did my (abortive) procedural world generator, I tried summing Perlin noise with a circle, and treating values below a certain threshold as water, with values above the threshold as land. That worked okay, because it would give a believably jagged coastline with offshore islands, but it was still obviously just a circle when viewed at scale.

Does anyone have a quick-and-dirty idea for partitioning land and water?


I made a generator for non-infinite worlds(specifically, Ace of Spades 0.x, which is a 512x512x63 colored-cube space) that created "faucets" for land and water, flood filled a low-res "biome" tilemap from them, and then applied some noise and interpolation to the tilemap, plus some heightmap modification to make a river. The larger number of passes creates a very "hand-generated" result, although a careful observer will discern that the edges are all slightly rectangular. One of the major benefits of the biome approach is that I could define the average height and deviations of the heightmap, making it easy to position mountains adjacent to flat open fields.

I've never seen the Perlin approach as being sufficient for making terrain that is _realistic_, since real terrain is shaped through iterative processes, not analytic ones.

[0] Example: http://imgur.com/txVfRYV


http://www-cs-students.stanford.edu/~amitp/game-programming/... was a springboard for many people dealing with this problem. That whole site is worth a read if you're interested in game development at all.


I really like that article - in fact, it's what I followed, using the Voronoi polygons and everything. But when it comes time to draw the coastline, the author just says "there are many ways to do this," without specifying what those ways are (other than using a pizza box or another source).


The real world partitions land and water by treating values below a certain threshold as ocean. That's not the problem.

There are tons of good terrain generators. Try Google. VistaPro, from the 1980s, is the classic.[1]

So far, nobody has built a procedural city generator good enough to generate a detailed city at ground level. This is one of the better ones, but the result comes out looking like a modern prison.[2]

[1] https://en.wikipedia.org/wiki/VistaPro [2] http://orphancity.tumblr.com/


Introversion were working on a game (Subversion) which has since been shelved indefinitely. Their city generation videos were amazing: https://www.youtube.com/watch?v=9pR8jpK4ETk


There's been recent progress. See Esri City Engine.[1] This is approaching GTA level cities.

[1] https://www.youtube.com/watch?v=aFRqSJFp-I0


In infinite generation, it's tricky to meaningfully do it. If you can constrain your space, I've had good luck with Voronoi generation and using those as continental plates. A little edge randomness, and I figure out what subducts, what separates, which plates are water and which plates aren't, and so on. Couple this with an iterative erosion mechanic and you can do some neat stuff.

("Look, this is Perlin noise" made this a disappointing article for me. It's the sort of thing you saw on GameDev.Net fifteen years ago...)


Voronoi polygons as plates is probably going to be the best way for me to go - rather than adding a circle, we can just add the distance from the nearest "land" polygon, and sum the noise with that. It doesn't get us interesting geology (which it sounds like you're doing brilliantly) but it will avoid the thousands of little rivers without oceans that we see with straight Perlin noise.


Another way is to tile Simplex 4D fractals, with the different levels generating different scales of detail, then do erosion-by-way-of-flooding due to heightmap/rainshadow calculations.


That's a really interesting idea, but I'm having trouble visualizing it. Have an example in action?


Oh, that was for an old project I'm not working on anymore. I'm experimenting a similar thing to create a "modern world generator" thing for a survival roguelike (a heavy tendency toward squared polys in many places, breaking up much more in suburban and rural areas).


This isn't directly relevant, but a while ago (nearly 2 years, apparently ._.) I wrote a thing that generates islands based on height contour lines and heightmaps based on them...

https://dl.dropboxusercontent.com/u/1094010/island_compiled....


What goes wrong if you just use Perlin noise for a height map and pick a "sea level" threshold? That also has the advantage of giving you an interesting seabed, if you have the ability to see underwater.

You'd still want various kinds of local mutation to create interesting features, though. For inspiration, you might look at how games like Angband and Crawl combine procedurally-generated overall level design with pre-constructed "vaults". A similar technique, with enough variations to avoid looking obvious, could work to embed more unusual features in an otherwise procedurally-generated set of continents.

Beyond that, you might also want a dedicated coastline-and-river generator, which picks appropriate high mountains, puts the top of a river there, and has it wend its way down altitude (eroding as needed) until it reaches the "ocean". Throw in some appropriate generation of beaches, riverbanks, and ocean cliffs.


Using Perlin noise with a sea level threshold is exactly what the OP and Minecraft both do. It makes the terrain more interesting and varied than what we see in real life, but the world as a whole doesn't have big oceans or continents - just little worms and snakes of land and water twisting around each other.

I had tried to avoid this by doing Perlin noise + distance from the center of the map, which makes an approximately-circular "continent" with an okay coastline. Amit Patel's article says "we can draw the coastline any number of ways," which is true - I'm just wondering what those ways are.


Sorry about that. What I meant by "any number of ways" is that the way I generate elevations works on any shape of coastline. In the demo, the default is to generate the coastline the way you describe — Perlin noise minus distance from the center of the map. I also have a "Radial" option that draws a bunch of sine waves in polar coordinates, and a "Blob" option that draws my blob logo. It would work just as well if you hand-draw it or use a pizza box or scatter macaroni. I should probably add an explanation to the article.


Well, if you had a mathematical model of continent formation from actual geology, you could use some of the noise generation to obtain values for the model's free parameters, and then just run the simulation forward (possibly approximating it very cheaply since you don't need maximal real-world accuracy) to get your continents.


Theres an article series where the author explores this for a little bit, but with water droplets affecting the terrain for erosion[0].

It gets better as he goes along as he talks about adding various terrain features, and eventually also rivers which cut through the landscape.

[0] http://www.shamusyoung.com/twentysidedtale/?p=11874


(Author here): One approach is to use multiple layers of noise configured with different parameters. By joining multiple layers you can give the appearance of things like ocean, flatlands, and mountains.

Also, cannot recommend Amit Patel's stuff enough. That guy is a fountain of knowledge for all things gamedev.


I did this for a project a while back with reasonable results. The algorithm was this: Test each tile for distance to water (you don't have to get this absolutely perfect, just check in about 8 different directions from the centre of the tile). The closer the tile is to water, apply a scaling factor to the elevation, such that at MAX_DISTANCE you scale by 1, at 0 (water's edge) you scale by a factor of 0 (ie you will always have 0 elevation. The exact curve of the scaling factor can be played around with to give you sharper or smoother coastlines... You can even slowly vary the scaling function so that you can have everything from river deltas to cliffs in different parts of the world.


Putting a probabilistic bell curve on top of simplex noise works generally well.

Here's what I did: https://thefiletree.com/espadrine/art/island.html?plug=none


The last time I made a world generation algorithm for a voxel engine prototype I got reasonable results through the judicious combination of different types of noise, (specifically simplex, ridged and turbulence noise) and transforming the result using cubic hermite splines for added control, provided you are okay with generally only having one or two main continents (three and four do occur, but are much less common).

This image shows 16 possible world maps (I found it useful to just have them appear next to each other in the world so I could quickly get an overview of the consequences of tweaking parameters): http://imgur.com/dthr7O2

This is the algorithm that generated the world (written in C#). permutation is a random ordering of the numbers 0 to 255, repeated twice. HermitePoints take an x and y coordinate and a slope. Noise functions take an x and y coordinate, a number of octaves, a frequency and a permutation. Turbulence and ridged noise output results in the domain [0, 1], simplex noise in the domain [-1, 1].

  private static Chunk GenerateChunk(short chunkX, short chunkZ, int seed, byte[] permutation)
  {
      var chunk = new Chunk(chunkX, chunkZ);

      int worldMapSize = 256;
      short maxHeight = (short)Math.Min(worldMapSize >> 2, Chunk.CHUNK_HEIGHT);

      float[] worley = new float[2];

      HermiteSpline continentCurve = new HermiteSpline(new[]
      {
          new HermitePoint(0f, 0f, 0f), new HermitePoint(0.07f, 0.03f, 1f), new HermitePoint(0.12f, 0.1f, 0.7f),
          new HermitePoint(0.21f, 0.18f, 1f), new HermitePoint(0.24f, 0.2455f, 0f), new HermitePoint(0.26f, 0.26f, 1f), new HermitePoint(1f, 1f, 1f)
      });
      HermiteSpline continentMask = new HermiteSpline(new[]
      {
          new HermitePoint(0f, 0f, 0f), new HermitePoint(0.25f, 0f, 0f), new HermitePoint(0.4f, 1f, 0f), new HermitePoint(1f, 1f, 0f)
      });
      HermiteSpline plateMountainCurve = new HermiteSpline(new[]
      {
          new HermitePoint(0f, 0f, 0f), new HermitePoint(1f, 1f, 3.5f)
      });

      for (short x = 0; x < Chunk.CHUNK_SIZE; x++)
      {
          for (short z = 0; z < Chunk.CHUNK_SIZE; z++)
          {
              int globalX = (chunkX << Chunk.CHUNK_SIZE_LOG2) + x;
              int globalZ = (chunkZ << Chunk.CHUNK_SIZE_LOG2) + z;

              //Subdive the world into squares, each of which contains an independent world map
              //The edges of each square are lowered so that each map is separated by oceans
              float xSeparator = (float)Math.Sin((globalX & (worldMapSize - 1)) * MathHelper.Pi / worldMapSize);
              float zSeparator = (float)Math.Sin((globalZ & (worldMapSize - 1)) * MathHelper.Pi / worldMapSize);
              float rectSeparator = (float)Math.Min(1, 3 * Math.Min(xSeparator, zSeparator));
              float circleSeparator = xSeparator * zSeparator;
              float mapSeparator = rectSeparator * 0.375f + circleSeparator * 0.575f + 0.05f;

              //Use turbulence noise to get the typical clumped shape of continents and add some simplex and ridged noise for the thinner shapes
              float continentNoise1 = Noise.Turbulence(globalX, globalZ, 8, worldMapSize, permutation);
              float continentNoise2 = Noise.Simplex(globalX, globalZ, 8, worldMapSize * 0.16f, permutation) * 0.5f + 0.5f;
              float continentNoise3 = Noise.Ridged(globalX, globalZ, 8, worldMapSize * 0.5f, permutation);
              continentNoise2 *= continentNoise2;
              continentNoise3 *= continentNoise3;
              float continentHeight = continentNoise1 * 0.5f + continentNoise2 * 0.25f + continentNoise3 * 0.125f;
              float baseHeight = continentCurve.Map(continentHeight * mapSeparator);

              //Add mountains caused by convergent continental plate boundaries
              float continentMult = continentMask.Map(baseHeight);
              float plateMountainNoise1 = Noise.Ridged(globalX, globalZ, 8, worldMapSize * 0.4f, permutation);
              float plateMountainNoise2 = Noise.Simplex(globalX, globalZ, 8, worldMapSize * 0.2f, permutation) * 0.5f + 0.5f;
              float plateMountainNoise = plateMountainNoise1 * 0.66f + plateMountainNoise2 * 0.33f;
              float plateMountainHeight = plateMountainCurve.Map(plateMountainNoise) * continentMult;

              //Apply the map separation
              float finalHeight = baseHeight + plateMountainHeight;

              //Convert the height from range [0,1] to range [1,255]
              byte height = (byte)(finalHeight * (maxHeight - 1) + 1);

              for (short y = 0; y < height; y++)
              {
                  chunk[(short)x, y, z] = (byte)((y + 1) * 255 / (maxHeight + 1));
                  chunk.SetStack((short)x, z, new short[] { height, (short)(Chunk.CHUNK_HEIGHT - height) });
              }
          }
      }

      return chunk;
  }


The downside of Simplex noise is that anything greater than 2D is patented: http://en.wikipedia.org/wiki/Simplex_noise There is another project called OpenSimplex that has similar results.


Not in europe :)


Ha, touché.


    Once you have access to random 2D data, it’s pretty straight forward to convert that noise into usable data. Simply set up thresholds for each tile type you want to support, eg:
     
    * water if < 0.3
    * grass if >= 0.3 and <= 0.6
    * mountain if > 0.6
     
    I recommend leaving the upper and lower cases open. If they are not left open, you may end up with holes in your map for unexpectedly high or low values. An alternative is to clamp or scale the noise values when you generate them.
If you expect to have more tile types, you should probably consider a categorical probability distribution (something like: water: 0.3, grass: 0.3, mountain: 0.4) and sample from it (instead of "hard-coding" it with the test you're referring to)


I like your idea here. I think the downside of this approach is you must scale your random data points by a scaling factor to get them in a known range.


What good luck I have! I have been fiddling with a procedurally generated 3d world and there are all these great articles about procedural art up on HN. Anyone know of an example project for a procedural 3d world?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: