Unlike John Carpenter’s movie, this blog post will not be about Ghost Pirates.
Chess players have total knowledge of the game state. They know where their opponents pieces are and can plan accordingly. In RTS games it is important that this is not the case. Players must be allowed time to build their armies and defences in secret; a match could be over in minutes if everyone knew exactly where each others bases are and what they are doing (looking at you grenadiers). Not knowing where the enemy is creates suspense and rewards players who bother to scout.
The way we can achieve this is to produce something called ‘fog of war’. This fog covers the entire game world and is dispersed by troops as they traverse the field. Think of it as the Mist from the movie of the same name. The characters are our units, we want them to be able to see only a certain distance ahead.
There are two ways to create fog of war, one is computed on the CPU and another on the GPU. I chose to implement the former, as it seemed easier to understand and implement. Programmers love to follow the KISS principle. No, that does not mean putting on makeup and listening to ‘Rock And Roll All Nite’.
Sometimes we end up having to redo our work. I began by creating the fog, which was basically a black plane sitting above the game world, and corresponded to an array of fog tiles. Next I created a script that would be added to each unit. This script adds the unit to a list of ‘fog effectors’, and while the unit is moving, finds the intersection on the fog plane between our camera and the unit. We would then use this point as the centre of our circle of dispersal. This circle was then used to gather coordinates and compare that to a list of fog tiles, if the tiles were ‘fogged’ they were added to a list.
At first this process worked quite well. The system cycled through each effector and ‘dispersed’ the appropriate fog tiles. There was one problem though. With RTS games its quite common to have hundreds of units on screen at once. This test proved fatal. Frame rate dropped to around 1.7 frames per second. Which is not playable. At all. We had to do better!
After some research, and a fair few forum posts later, I found that a change of plan would be in our best interest. This was implemented in a similar way, only the computation was delegated to the hardware through the use of shaders. A map object was created containing the size and position of the game world. This was then passed into the ‘drawer’ which set properties for the shaders to do their job of covering the map in a glorious fog. Luckily I was able to get quite a bit of assistance with the shaders, as they are dark boxes of mysticism that only a wizard can make sense of.
Each team in a match is assigned a fog of war object, with only the local players’ fog rendered on screen. This lets our determinism system incorporate fog checks for other players’ units, and includes a shared fog for team-mates.
This solution also uses the position of the unit as it moves to determine what to disperse. This time we incorporated unit ‘line of sight’ (LOS), allowing us to create a flashlight effect, and stop dispersal when the unit is obstructed by a wall, for example.
We also use shaders to create the shape around the unit for fog dispersal. This shape is also cached so it does not need to be recreated every tick.
With this completed, and the great purge [refactor] (which Richard alluded to last month) coming to conclusion, we are now in a position to create the UI. I have told Richard this will take hours of
playing games research before I can get cracking.