Dearest of readers.
If you're looking to find out how math can help you built beautiful random environments like
well, this is your post. Better hold on: curves ahead (nah, not really).
Each time you play Sticks & Stones new maps are generated. That's so the player isn't able to memorize them and has to instead learn how to beat each stage from playing again and again. It's a quite simple game in fact (easy to play, hard to master, remember ;) ); the same stuff happens again and again: craft, beat the boss, craft, beat the other boss, craft, beat the other boss, that's it (don't worry if it sounds too simple, you can reduce any game you love to a few verbs, try it with Uncharted, or Nuclear Throne) To make that work, however, variety is a must.
How then? You could set yourself to make them one by one, each with its quirks and traps and whatnot and its own character and flow, and then add some code that stores what versions of a map have been played. That's it. That's cool, that's actually a good idea I believe, but also a huge undertaking, and something we're not capable of doing unless we decide to eat Ramen for the next eight years.
Enter procedural generation.
Procedural generation, for those who don't know (and seriously, who doesn't by now), means that you use algorithms to create data rather than yourself: yeah, math. It's math. It's not the same as random generation, where everything goes, but that you establish some rules from which stuff, given an initial seed, grows, getting unexpected results, but only up to a point.
Someone in our Greenlight page (yeah, we're on Greenlight: Go and vote!) pointed out that our although the game looks cool, the maps look "bland and uniform". That's because at this stage we've only skimmed the surface of our Procedural Generation engine, setting just the basics of terrain and assets distribution (we're not worried about that, we have a couple of ideas on how to make them more interesting, atmospherics maybe, scarcity, minions, setting probabilities for some special crafting objects...). The user's right in saying also the it looks like we're using a simple Noise Function like Perlin Noise: and that's exactly right! We are using a simple noise function and it is Perlin Noise, but that's just the beginning!
Now, our Perlin Noise implementation works like this:
It uses 3 parameters from which you can change the final appearance of the wave (and therefore of the terrain): For a given time interval
- Frequency: sets how many waves should appear in that interval.
- Amplitude: sets the how high will they be.
- Octaves: sets how smooth will the terrain, or rather, how much uniformity and detail you want in the wave. The higher the octaves, the more detail you have. ("Why is it called 'Octaves'", you might ask. This site gives an answer: "These coherent-noise functions are called octaves because each octave has, by default, double the frequency of the previous octave. Musical tones have this property as well; a musical C tone that is one octave higher than the previous C tone has double its frequency.")
So. How do we get maps from that? For Sticks & Stones, we start with a 3 x 3 grid. XY stands for pixel coordinates, and Z for height. Our Perlin Noise implementation then does all it's mumbo jumbo
MatrixMapGenerator.Generate2DNoiseTiled( zoneParameters.MapWidthScaled, zoneParameters.MapHeightScaled, MapWorld.SEED, zoneParameters.TotalNumColumnsMapPieces, zoneParameters.TotalNumRowsMapPieces, perlinParams.Frequency, perlinParams.Amplitude, perlinParams.Octaves, MaxHeightValue);
To obtain something like this (for the same set of parameters):
(with some false color for height)
We then establish a relation between height and texture, following the same principles of topographic maps:
- [∞ .. 300] = Grass
- [0 .. -200] = Dirt
- [-500 .. -700] = Rugged dirt
- [-900 .. ∞] = Water (10% alpha)
Which means, for example, that for any given pixel if it has a height value of 250, it has to be painted as grass. Each texture as:
Mix it up in the Perlin Noise Shaker and, voilá:
Play with the values and you start getting quite different results:
Amplitude = 1
That translates into:
With Frequency = 1, you get wider areas, less variation.
Octaves = 1, you get much smoother height transitions
With those simple steps we have the first layer of variation in Sticks & Stones. We've got lava, snow, meadows, deserts
Furthermore, we could use that height to sprinkle the terrain with objects, and set some rules so that we don't get incoherent stuff, like a big tree in the middle of a patch of dirt, but rather a tree right next to other trees, as in trying to convey a forest
- [∞ .. 500] = Trees
- [500..0] = Plants, flowers
- [0 .. -200] = Rocks
- [-500 .. -700] = Stones, dead trees
- [-900 .. ∞] = Cattails
Now it all seems organic and natural. If we play with the height, then the terrain responds to that!
Add some light effects, some blur, some sugar, some spice and it all looks now rather nice:
So there it is! That's how or Procedural Generation's going so far.
Be sure to leave a comment or share if you like and to vote in Greenlight!