DevLog #3: Now You See Me

Despite their variety in setting, there is one crucial resource most, if not all strategy games equally share – knowledge from map reconaissance. And what is the natural enemy of that? A Fog Of War of course!

What do we expect from a good Fog Of War?

  • Cells and their contents should be revealed when units move into or close to them
  • When losing sight of a cell it should be hidden again, but some basic information about the cell should be preserved (e.g. the terrain type, positions of enemy locations etc.)
  • Information on previously revealed cells should not get updated until the player reveals that particular cell again

The basic techniques of my Fog Of War is based on this blog post by Ariel Coppes. Their original idea was implemented in a real-time strategy game but most of its principles work fine in a turn-based strategy game as well. To make it all work, we need three ingredients attached to each player.

The Visibility Matrix

This is pretty similar to what Ariel describes in the original post. Part of the player script is a two-dimensional array of integer values.

Meanwhile, each actor that should have any kind of influence on visibility has a BaseViewRange property which (obviously) holds information about the range of cells this unit can oversee on the map. The range can further be influenced by other things happening around the actor – a unit positioned on a mountain tile could get a temporary bonus on its view range, a location could expand its view range depending on its current tier etc.

The not-yet-so-glorious outpost of Kevinsby with a measly view range of two cells…

Whenever a unit is placed on the map the game uses this value to determine which cells a unit can observe. For each of these cells, the visibility level in the matrix is raised by 1, and the cell and all its contents are revealed.

Visualized visibiliy levels stored in the matrix array

If the unit moves away from its current cell, the visibility value of that cell is reduced by 1 again. Once it reaches 0 the cell and all its contents are hidden again.

This way it does not matter if a cell was observed by more than one unit – we just raise the value whenever another unit can “see” the cell in question, and it will remain visible until the last unit moves away and the visibility level will eventually reach 0 again.

The previously visible forest in the Southeast has it’s visibility level reduced to 0 again, while the forest cell seen by both units has a combined visibility level of 2 – one for each unit

However, as you can see in the above screenshot cells no longer visible do not become unknown territory again but are merely shrouded instead, preserving the player’s knowledge of the terrain type they just uncovered.

The Map Reveal Matrix

But how do we keep track of this information when the visibility value is 0 again? Sure, we could just change the cell’s colour and switch it back as soon as another unit steps within range, but that “information” would be lost if we decided to save and exit the game to continue our session another time.

This is where the Map Reveal Matrix comes into play. Again, this is merely a two-dimensional array of integers, but instead of the visibility level this array keeps track of whether or not the cell at the array’s coordinates has been revealed by the player at least once during the session.

The same scene with the reveal values instead – the forest area in the Southeast is no longer visible but remains “known”, so the reveal value stays at 1, while the forest between both units does not care about how many units observe it at the same time

Since this is a simple array it can easily be serialized and stored in a save game for later reconstruction – something we don’t need to do with the Visibility Matrix by the way, since we can easily recreate its contents from the current actor positions when reloading a previous session.

The Ghost Sprite Matrix

One convention in strategy games is that, while units disappear from the player’s vision when covered by the Fog Of War again, knowledge about locations or other fixed structures is preserved. While its citizens will certainly move on with their lives, a city is likely to be in the same spot the next time you visit.

“Fine,” you might say, “we just don’t hide the city’s sprite but merely shroud it instead like we did with the terrain.”

Our little bat came across the outpost some time ago…

However, this introduces a couple of issues.

  • When an opponent upgrades one of their cities, players will know about that even though the location’s cell might be shrouded at that time
  • Likewise, when a city is destroyed it will instantly disappear from the map even though the player would not know about that
  • Your CPU opponents don’t care about sprites and cannot process that kind of (non-)information
… but does not know it has since expanded and been upgraded to a thriving metropolis!

Enter the Ghost Sprite Matrix. Unlike the first two this is not an array covering the whole map but a dictionary instead, its keys being the HexCell in question and the values of type GhostSprite.

Whenever a player loses visual of a cell that contained a location, a GhostSprite instance is created which – among some other things – includes information about the type of structure contained at the time the cell disappeared under the Fog Of War again. Meanwhile, the original location sprite is hidden just like any other actor.

Should your opponent decide to upgrade this location they now can do so without being spotted – the player does not see the location’s actual sprite but will only see that of the GhostSprite created the last time they had proper information about its cell.

The next time the player gets in visibility range of the cell the GhostSprite is removed to make room for the location and its current sprite again.

Final Thoughts

The main advantage of this approach is that everything the player sees is merely a visualization of data stored in two arrays and a dictionary, both of which can be stored in a save game for later reconstruction. And since everything is just data, CPU opponents can be placed under the same limitations as the player – if they don’t see it, they don’t know about it.

Another benefit of storing this information for each player separately is that it also works in multiplayer or even hotseat mode with two players taking turns in front of the same PC. As soon as a player ends their turn each cell’s visibility is updated according to the next player’s visibility, reveal, and ghost sprite matrix.

Meanwhile, if you want to play a session without an active Fog Of War you only need to set visibility to 1 and skip the addition/subtraction parts.

Leave a Comment