Simple Map Generator Part 4 ( Rivers )

So now that we’ve calculated elevations and somewhat created the Y axis on our map , we can now create some rivers! Rivers will turn or boring looking island into a much better , interesting environment. We’ll also use rivers and watersheds to calculate moisture and biome later on so we definitely need them.

 We’ll use a simple method to generate them , we’ll get a few random points on the map and then create the river down to the ocean or a lake using the elevation datas. The number of rivers and the starting points will all be random for now.

 

First of all , we’ll iterate through all corners and create a “Downslope” property for them. Downslope will be the adjacent corner with the lowest elevation or in other words , the way which water will run.

Then we’ll do pretty much same for the calculation of watersheds. Iterate through all corners and set the Watershed of the Downslope corner as the current corners Watershed until it reaches water.

Then we’ll actually create the rivers , take a random corner between statically set elevations ( like 0.3 and 0.9 , since we don’t want rivers starting at the beach or at the top of the mountain ) , follow downslopes till water and calculate river width depending on the watersheds we calculated earlier.

 

Huh I don’t even know why did I bother to explain it like this , it should be much more easy to explain with code!

 

So here we go , our first method , CalculateDownslopes!

private void CalculateDownslopes()
{
    foreach (Corner corner in App.AppMap.Corners.Values)
    {
        var buf = corner;
     
        foreach (var adj in corner.Adjacents)
        {
            if (adj.Elevation <= buf.Elevation)
            {
                buf = adj;
            }
        }

        corner.Downslope = buf;
    }
}

Just iterate through corners and set the adjacent with lowest elevation as Downslope. I don’t know if there are anything else to say really , so I’ll just move on to the next method , CalculateWatersheds ,

private void CalculateWatersheds()
{
   
    foreach (var q in App.AppMap.Corners.Values)
    {
        q.Watershed = q;
       
        if (!q.Ocean && !q.Coast)
        {
            q.Watershed = q.Downslope;
        }
    }
   
    for (int i = 0; i < 100; i++)
    {
        var changed = false;

        foreach (var q in App.AppMap.Corners.Values)
        {
            if (!q.Ocean && !q.Coast && !q.Watershed.Coast)
            {
                var r = q.Downslope.Watershed;
               
                if (!r.Ocean)
                    q.Watershed = r;
               
                changed = true;
            }
        }

        if (!changed)
            break;
    }

    foreach (var q in App.AppMap.Corners.Values)
    {
        var r = q.Watershed;
        r.WatershedSize = 1 + r.WatershedSize;
    }

}

This looks a bit more complex , but it’s just 3 simple iterations really.

First we set the downslope of each corner also as watershed. This will be the first step towards water.

Second iteration is a bit weird and I’m not really sure about the implementation,  yet I’ll just keep it as it is in the original code written by Amit. What it does , is to follow downslopes until it reaches coast or any water source. When it’s done for all corners , it’ll create a structure like a tree , for watersheds.

What I wasn’t sure about is if we really need to iterate through all corners many times like that instead of a depth first like iteration. That “for” iterator from 0 to 100 will break very early so we don’t go all the way up 100 of course but even 5 iterations means Corner count * 5 steps after all. Anyway , as I said I’ll just stick with the original code here.

And in the 3rd iteration , we’ll just increase the watershed size. Remember more than one corners may have same corner as watershed so this will increase the watershed size of that central corner.

 

And last but not least , the CalculateRiver method ,

private void CreateRivers()
{
    var rnd = new Random();

    for (int i = 0; i < MapX / 2; i++)
    {
        Corner q = App.AppMap.Corners.Values.ElementAt(rnd.Next(0, App.AppMap.Corners.Values.Count 1));
       
        if (q.Ocean || q.Elevation < 0.3 || q.Elevation > 0.9) continue;
       
        while (!q.Coast)
        {
            if (q == q.Downslope)
            {
                break;
            }

            Edge edge = q.Protrudes.FirstOrDefault(ed => ed.VoronoiStart == q.Downslope || ed.VoronoiEnd == q.Downslope);
            edge.River = edge.River + 1;
            q.River = q.River + 1;
            q.Downslope.River = q.Downslope.River + 1;
            q = q.Downslope;
        }
    }
}

We first start with getting a Random but I highly suggest using a centralized Random generator instead of creating a new one , so you can feed any number to that centralized Random as seed and recreate any island you want just by a number.

Then we’ll grab some random point , for some reason Amit preferred to do this in a “for” iterator , I’m not a fan of that personally. Too random for my liking I guess.

Then when we get these corners , we’ll use them as starting points for rivers and follow downslopes until we reach water. And with each step , we’ll increase the River value of the edges and corners. This will give us a nice effect , rivers joining together and getting bigger.

 

Hmm guess that’s all. after all these 3 methods , you’ll have something like this ,

WindowClipping (6)

 

Much better , isn’t it?

Next , calculating moistures I believe. I’ll try to write that as soon as possible too.

You can find the sample solution file below , hope you like it!.

 

Visual Studio 2010 Solution

2 thoughts on “Simple Map Generator Part 4 ( Rivers )

Leave a Reply