views:

166

answers:

5

I'm toying around with writing a random map generator, and am not quite sure how to randomly generate realistic landscapes. I'm working with these sorts of local-scale maps, which presents some interesting problems.

One of the simplest cases is the forest:

                    Sparse  Medium  Dense
Typical trees       50%     70%     80%
Massive trees       —       10%     20%
Light undergrowth   50%     70%     50%
Heavy undergrowth   —       20%     50%

Trees and undergrowth can exist in the same space, so an average sparse forest has 25% typical trees and light undergrowth, 25% typical trees, 25% light undergrowth, and 25% open space. Medium and dense forests will take a bit more thinking, but it's not where my problem lies either, as it's all evenly dispersed.

My problem lies in generating clusters and paths, while keeping the percentage constraints. Marshes are a good example of this:

                    Moor  Swamp
Shallow bog         20%   40%
Deep bog            5%    20%
Light undergrowth   30%   20%
Heavy undergrowth   10%   20%

Deep bog squares are usually clustered together and surrounded by an irregular ring of shallow bog squares.

An additional map element, a hedgerow, may also be present, as well as a path of open ground, snaking through the bog. Both of these types of map elements (clusters and paths) present problems, as the total composition of the map should contain X% of the element, but it's not evenly distributed. Other elements, such as streams, ponds, and quicksand need either a cluster or path-type generation as well.

What technique can I use to generate realistic maps given these constraints?


I'm using C#, FYI (but this isn't a C#-specific question.)

+2  A: 

I've never done this sort of thing, but here are some thoughts.

You can obtain clusters by biasing random selection to locations on the grid that are close to existing elements of that type. Assign a default value of 1 to all squares. For squares with existing clustered elements, add clustering value to to adjacent squares (the higher the clustering value, the stronger the clustering will be). Then do random selection for the next element of that type on the probability distribution function of all the squares.

For paths, you could have a similar procedure, except that paths would be extended step-wise (probability of path is finite at squares next to the end of the path and zero everywhere else). Directional paths could be done by increasing the probability of selection in the direction of the path. Meandering paths could have a direction that changes over the course of random extension (new_direction = mf * old_direction + (1-mf) * rand_direction, where mf is a momentum factor between 0 and 1).

academicRobot
A: 

You could start reading links here. I remember looking at much better document. Will post it if I find it (it was also based on L-systems).

But that's on the general side; on the particular problem you face I guess you should model it in terms of

  • percentages
  • other rules (clusters and paths)

The point is that even though you don't know how to construct the map with given properties, if you are able to evaluate the properties (clustering ratio; path niceness) and score on them you can then brute force or do some other problem space transversal.

If you still want to do generative approach then you will have to examine generative rules a bit closer; here's an idea that I would pursue

  • create patterns of different terrains and terrain covers that have required properties of 'clusterness', 'pathness' or uniformity
  • create the patterns in such a way that the values for deep bog are not discreet, but assign probability value; after the pattern had been created you can normalize this probability in such a way that it will produce required percentage of cover
  • mix different patterns together
Unreason
+11  A: 

Realistic "random" distribution is often done using Perlin Noise, which can be used to give a distribution with "clumps" like you mention. It works by summing/combining multiple layers of linearly interpolated values from random data points. Each layer (or "octave") has twice as many data points as the last, and confined to a narrower range of values. The result is "realistic" looking random texture.

Here is a beautiful demonstration of the theory behind Perlin Noise by Hugo Elias.

Here is the first thing I found on Perlin Noise in C#.

What you can do is generate a Perlin Noise image and set a "threshold", where anything above a value is "on" and everything below it is "off". What you will end up with is clumps where things are above the threshold, which look irregular and awesome. Simply assign the ones above the threshold to where you want your terrain feature to be.

Here is a demonstration if a program generating a Perlin Noise bitmap and then adjusting the cut-off threshold over time. A clear "clumping" is visible. It could be just what you wanted.

Notice that, with a high threshold, very few points are above it, and it's sparse. But as the threshold lowers, those points "grow" into clumps (by the nature of perlin noise), and some of these clumps will join eachother, and basically create something very natural and terrain-like.

Note that you could also set the "clump factor", or the tendency of features to clump, by setting the "turbulence" of your Perlin Noise function, which basically causes peaks and valleys of your PN function to be accentuated and closer together.

Now, where to set the threshold? The higher the threshold, the lower the percentage of the feature on the final map. The lower the threshold, the higher the percentage. You can mess around with them. You could probably get exact percentages by fiddling around with a little math (it seems that the distribution of values follows a Normal Distribution; I could be wrong). Tweak it until it's just right :)

EDIT As pointed out in the comments, you can find the exact percentage by creating a cumulative histogram (index of what % of the map is under a threshold) and pick the threshold that gives you the percent you need.

The coolest thing here is that you can create features that clump around certain other features (like your marsh features) trivially here -- just use the same Perlin Noise map twice -- the second time, lowering the threshold. The first one will be clumpy, and the second one will be clumpy around the same areas, but with the clumps enlarged (refer to the flash animation posted earlier).

As for other features like hedgerows, you could try modeling simple random walk lines that have a higher tendency to go straight than turn, and place them anywhere randomly on your perlin-based map.


samples

Here is a sample 50x50 tile Sparse Forest Map. The undergrowth is colored brown and the trees are colored blue (sorry) to make it clear which is which.

Sparse Forest

For this map I didn't make exact thresholds to match 50%; I only set the threshold at 50% of the maximum. Statistically, this will average out to exactly 50% every time. But it might not be exact enough for your purposes; see the earlier note for how to do this.


Here is a demo of your Marsh features (not including undergrowth, for clarity), with shallow marsh in grey and deep marsh in back:

Marshes

This is just 50x50, so there are some artifacts from that, but you can see how easily you can make the shallow marsh "grow" from the deep marsh -- simply by adjusting the threshold on the same Perlin map. For this one, I eyeballed the threshold level to give the most eye-pleasing results, but for your own purposes, you could do what was mentioned before.

Here is a marsh map generated from the same Perlin Noise map, but on stretched out over 250x250 tiled map instead:

Marshes 250x250

Justin L.
The threshold can be exactly calculated for a given perlin noise bitmap. Make a histogram and from the histogram you can calculate the cut off point (range to keep) that will result in any percentage (regardless of the distribution); you can even do this in several ways.
Unreason
Unreason - thanks; it looks like I completely misread the question to think that the asker wanted **percentage of total features** instead of **percentage of map covered**. I'll update my answer =)
Justin L.
I love the samples! If I understand correctly - you generated two Perlin Noise maps for the forest (one for the brush, and one for the trees,) and overlayed them? Then for the marshes, you generated one map, and used slightly different thresholds? Did you use the code from [gutgames](http://www.gutgames.com/post/Perlin-Noise.aspx)? If so, do you what know the purpose of the octaves is? None of the other Perlin Noise code I've found has loops like that in it, and I can't figure it out.
Daniel Rasmussen
Also, an average percentage is perfectly fine - I'll probably use a threshold of 50% as well (or something similar.)
Daniel Rasmussen
Sorry - I missed the new link you had added. Thanks!
Daniel Rasmussen
You're correct in what I did for each sample. I didn't use the code from gutgames, actually; what I did was generate a smooth Perlin Noise map as a greyscale image and then applied some Photoshop filters, but you should get the picture. I hope the new link (from [hugo elias](http://freespace.virgin.net/hugo.elias/models/m_perlin.htm)) helped you clear some things up =)
Justin L.
I've run into another caveat - how can I use Perlin Noise to generate Medium and dense forests? I could use the same method as with marshes, but then massive trees would only be found in the center of typical trees, which is not necessarily what I want. I can't use the same method as a sparse forest, because typical trees and massive trees can't overlap like undergrowth can. How can I use two Perlin maps to place exclusive objects, while retaining an approximate percentage?
Daniel Rasmussen
@Daniel: Two possible suggestions -- one, having the less-common one simply "overwrite" the other, and maybe, optionally, dynamically adjusting the threshold. The other is to sort of "interweave" the two, by, at each spot, flipping a coin to see which is actually placed.
Justin L.
After a few months on the back burner, I've begun working on this again, and have come upon another question: http://stackoverflow.com/questions/3696747/generation-of-uniformly-distributed-random-noise
Daniel Rasmussen
A: 

You might have some success for certain types of area with a Voronoi pattern. I've never seen it used to create maps but I have seen it used in a number of similar fields.

Ian Turner
+1  A: 

To expand on academicRobot's comments, you could start with a default marsh or forest seed in some of the grid cells and let them grow from the source using a correlated random number. For instance a bog might have eight adjacent grid cells each of which has a 90% probability of also being a bog, but a 10% probability of being something else. You can let the ecosytem form from the seed and adjust the correlation until you get something that looks right. Probably pretty easy to implement even in a spreadsheet.

Grembo