Sunday, September 20, 2020

Tile Workflow and Sub-Distributions

I have been rather busy this week in my professional work, so I have not spent as much time working on the game as I would have liked.  However, I did manage to tweak a few things and add a few new features to the tile engine that make things a little more interesting.  For starters, I was able to change up my spritesheet workflow to be more productive.  Take a look at this grass tilesheet.

Buffalo Grass

Spritesheet Workflow

I use a plugin tool in Paint.NET named Outline Object to create the black edges of each tile.  But due to the way in which the tool works, the only way to get the edges correct was to copy each single tile into another image, run the tool on it, and then copy and paste it back onto my original tilesheet.  I decided it was time to create a template image that would allow me to stamp the entire tilesheet at once - saving a ton of manual work.  There are essentially four steps involved in making or changing one of my top-level tilesheets.  First, I start with a base texture (top-left square).  Once I am happy with the look, I create an empty image that can hold the 16 variations of my texture.  Since my tiles are 100x100 pixels, the tilesheet image is 400x400 pixels.  Next, I duplicate my base texture to fill the first row, then duplicate that row to fill in the remaining three rows.  When that is done, I can really see whether the texture is tiling properly.  The less I can notice the edges of each tile, the better!  Then, I use a 400x400 pixel template image to stamp in the transparent edges of the tilesheet (all the parts in white above).  Finally, I stamp in all of the black edges to create a sense of elevation to the edges of the grass.  This new process is much faster than before.  Introducing the new tilesheet into the game is also now very easy thanks to the Scriptable Object changes I have recently made.  Feel free to read my previous posts if you are interested in Scriptable Objects.

Custom World Terrain Tab

Cluster-Spawned Special Tiles

Next on my list of changes this week was the distribution of the Special Tiles.  I use a Perlin noise algorithm to generate the game map.  But the Perlin engine only distributes the first two columns of tile types you see in the Terrain tab above.  The special tiles were intended to be distributed within the other tiles based on some hard-coded relationships between the regular tiles and the special tiles.  For example, Moss only spawns within certain grasses.  The result of the original distribution was not very organic, just a random Moss tile here and there.  I decided to write a method to distribute the special tiles in clusters rather than sprinkled around the map.  The cluster logic basically chooses a random number of cluster spawns and then determines how many tiles each cluster should have based on the total tile count of all possible compatible tiles on the map.  A random spot on the map is then checked to see if the special tile can spawn at the location, and if so, that tile becomes the spawn anchor point.  Another method randomly determines the placement of the next adjacent tile, resetting the spawn point, and the loop continues.  Notice in the next screenshot how the Brackish water has spawned clustered  together without looking like a simple circle or rectangle.

A Brackish Water Cluster

Once the spawn-point is checked, the method doesn't care what type the next tile is unless it is an edge-of-the-map tile, then it switches to another random 8-direction choice for the next tile.  This allows the special tiles to cross-over into other adjacent tile types creating a very nice organic feel.

Shuffling Distribution

Notice in the second screenshot above that some of the tile types are named in green.  I thought it would be more interesting to allow some of the tile types to be shuffled in order when they are distributed based on their Perlin values.  By checking the box, the green tile types will have their order on the map shuffled randomly.  For anyone interested, I created a list of tile-type id's and populated the list with the tile type id's in their original order.  To shuffle, I pass the list into an extension method which shuffles the order of the integers and returns the list in a random shuffled order. Then, looping through the random list allowed the distribution to be shuffled.  Here is an extension method which does the shuffling.  By the way, this method can shuffle any List<T>, not just integers.  I am sure I will use it in the future to randomize NPC names, and many other things.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static void Shuffle<T>(this IList<T> list)
        {  
            var rand = new System.Random();          
            int n = list.Count;
            while (n > 1)
            {
                n--;
                int k = rand.Next(n + 1);
                T value = list[k];
                list[k] = list[n];
                list[n] = value;
            }
        }

The following is a small map generated in the original un-shuffled order.  Then, the next map is one where the shuffle has been turned on.  Notice how the tile types have changed in order.  This creates some very nice differences between maps generated from the exact same Perlin data.  

Un-Shuffled Map

Shuffled Map
The keen eye will notice subtle differences between the distribution of tiles in the two maps.  This isn't a bug.  Each tile type has a weight value (the sliders on the Custom World screen).  The weights determine how much of the map each tile type takes. It is my goal to provide a way to re-generate the exact same map terrain given a seed value and the weight values from the map builder.  Now, I can add to this the shuffle list included in the map template.  This will permit creating identical maps given these values.  Well, that is all for now.  Until next time...

No comments:

Post a Comment

Feel free to leave a comment. All comments are moderated.