In a previous tutorial, we worked on our main camera and we implemented a translation-and-zoom system so we can move around our game view. In most RTS games, we also have a top view of the scene shown in a corner of the UI as a “minimap”. Another common feature in those games (present for example in Blizzard’s Starcraft or Warcraft series) is a mechanism called the “fog of war”: the idea is to limit the player’s vision of the terrain by only lighting up the areas within the range of vision of the player’s units.

Today, we’re going to start implementing these two features to our game — since it’s a bit complex and not as quick to setup as previous systems, I’ve separated this tutorial in three parts and we’ll continue working on it in the weeks to come ;)

In this first part, we’re going to setup the minimap in the UI and implement a fog of war that determines where our scene is lit and where it is kept in the dark.

Creating the minimap

For now, let’s forget about the fog of war and implement the minimap first. So if we suppose everything on the map is lit up, here is a preview of the final UI display of our minimap:

To create this minimap, we want to paint a top-view of our terrain on an image, in our UI. To get this view, we can use another orthographic camera — this time, we give it a 90° degrees rotation on the X axis so that it is looking at the ground directly, and we increase its orthographic size so it sees the entire terrain (remember that Unity’s terrains size can be configured on the “Terrain” component, in the settings tab — by default, those terrains are 1000 x 1000 wide). At this point, if you select the camera, you’ll get a preview that looks a lot like we want (see in the bottom-right corner of the “scene” tab)!

But the question is: how do we “transfer” this view to our UI? The process is the following:

  1. get the input stream from the camera
  2. re-paint it on an image
  3. place this image in our UI

We could do it “by-hand”, by getting the camera feed and creating a texture ourselves pixel by pixel… but this would be very inefficient, and way too complicated! Instead, let’s take advantage of Unity’s render textures. As explained in the docs, “a Render Texture is a type of Texture

that Unity creates and updates at run time” — so, basically, it’s going to enable us to automatically and continuously inject the top-view camera’s stream into this texture. And this texture can then be used directly in an image, just like any other ;)

To set this texture up, we need to first create a new “Render Texture” asset, then assign it to our minimap top-view camera. To be honest, I haven’t tweaked the settings a lot to try things out, so far it seems the base settings are alright:

Note: we could, however, reduce the size of the texture because, in the end, it won’t be displayed with a large image in the UI. But if you do, it’s better to keep a size with a power of 2 (like 64 or 128), and of course we want a square texture so we need to keep the width equal to the height.

Alright, we’re now ready to integrate this texture to our UI! It’s quite easy to do — you simply need to be careful to add a “Raw Image” UI component rather than an “Image”; and then, you can just drag the minimap render texture into its texture slot:

Adding a field of view to units and a fog of war

Disclaimer:_ this part of the tutorial is based on Andrew Hung’s “Implementing Attractive Fog Of War In Unity” tutorial. I’ve started from his idea of having 2 cameras render to 2 render textures, and then use them for direct projection; but I’ve reduced the number of asset and changed his shader a bit to directly blend the render textures together._

It’s now time to add our second feature: the fog of war. It is described for example in Warcraft 3’s wikipedia as “any area or section of the map that is covered by a grayed area”. It relies on our units having a given field of view (FOV), i.e. a range of vision that can be materialized by a disk around their position, that determines the zones on the map currently “in sight” for the player. We usually distinguish between three types of areas:

  • the “unexplored areas”: the zones on the map none of your units ever had in their field of view — it’s completely black and opaque
  • the “already explored areas”: the zones on the map your units once had but don’t currently have in their field of view — it’s darkened but semi-visible
  • the “in-sight areas”: the zones on the map your units currently have in their field of view — it’s lit up normally and shown without additional light filters

Creating the fog of war

First of, in his post, Andrew implements the field of vision (FOV) of the units. This is done by adding a custom mesh near/on the unit that is somewhat circular and sets the zone that should be in-sight around the unit. For us, the FOV mesh (that is put on a new “FOV” layer) can be added into our units prefab. We need to deactivate it at first so our units (especially the buildings) don’t have an active FOV yet before being placed on the ground. (Otherwise, the player could drag an in-construction building to the edge of the currently discovered areas and see more of the map)

Also, the FOV should probably have a different size depending on the unit — we can set this as a new variable in the UnitData class, and use it during the initialization of the Unit class. To get the best control on our FOV, we’ll also have a reference to our FOV sub-object in the UnitManager class, and we’ll create an EnableFOV() method in it. So let’s update our three scripts to add this logic:

Don’t forget to drag and drop the “FOV” game object in your prefab to its “BuildingManager” or “CharacterManager” newly created “Fov” slot :)

Then, as Andrew explains in his post, we can reuse render textures for the fog of war feature, just like we did earlier for the minimap. This time, we are going to use those textures not for direct viewing but instead to generate alpha masks that define the unexplored/already explored/in-sight areas on the map. The alpha masks will then be fed to a projector so it can cast light on the proper areas on the map.

So what are these alpha masks exactly? Well, they are just square images with black and white pixels: black means no light and white means full light. Basically, our units are going to mark those masks with their field of view so we now where the projector should cast light, and by definition this will automatically keep the rest of the map in the dark.

We need two render textures (and therefore two cameras, one rendering to each texture) so we can differentiate between “unexplored” and “already explored” areas:

  • the “explored areas” texture contains in white the zones that are currently in-sight while the rest is completely black
  • the “already explored areas” texture is a bit more tricky: the white corresponds to the areas currently and previously in-sight, which means that we need to store the camera render at each frame without clearing the previous frames

#development #games #tutorial #unity #csharp

Making a RTS game #13: Adding a minimap and fog of war 1/3 (Unity/C#)
1.95 GEEK