01 September 2024

"Creating an RTS Game in Unity 2023" by Bruno Cicanci

Book cover
In any learning process, there comes a point where you surpass the initial level and from there, you enter the intermediate stage. There are still many things you don't know, but you find that the available bibliography drastically reduces. There are many books to introduce you to the world of game development, but not as many for the intermediate-advanced level.

I started this book because the table of contents promised to cover topics not commonly found in other books on Unity, such as level creation, tool development through editor extension, multiple unit selection, formations, resource management, fog of war, or customizing builds. However, I had my reservations, given my past experiences with PacktPub books.

Fortunately, this book has proven to be above average compared to others from the same publisher. It indeed covers those topics and more, in a thorough and clear manner, using expressive and high-quality source code. I could tell that the author is a good developer and knows what they're doing. Thanks to this, I’ve learned a few new things along the way—things I didn't know and that will now enrich my way of developing in Unity. At this stage, that's no small feat.

To illustrate everything, the book develops a real-time strategy game, akin to War of Warcraft, with a top-down perspective. As you might imagine, it’s all done with free resources and in a mock-up mode, but I have to admit it touches on many aspects that should be considered in a game of that genre.

If I had to point out one downside, there was one feature I was particularly interested in, the fog of war, and I was disappointed that one of the elements used to implement it was a legacy feature. It’s impossible to guarantee that a technical book will remain up-to-date after a few years, but recognizing yourself, in your book, that you're going to use a legacy feature... I find that quite disappointing. Everything explained in a technical book should be current at the time of its writing.

Aside from that small detail, the truth is that it’s a recommendable book from which you can learn a lot. Highly recommended.

31 August 2024

How to implement a level map with two level of Fog Of War (Two level FOW) in Godot

Map with two level FOW
In my previous article, I explained how to implement a level map in Godot with fog of war. That map initially appeared black, but it gradually revealed itself as the character moved through it. It was a single-level map because, once the character traversed an area, that area remained fully uncovered, and all enemies passing through it were visible, even if the main character was far away.

Other games implement a two-level FOW, where the area immediately surrounding the character, within their line of sight, is fully uncovered, and enemies passing through it are entirely visible, while in areas left behind, only the static part of the map is visible, but not the enemies that might be there. Generally, maps with two-level FOW display distant areas with a dimmer tone, where only the static part is visible, to distinguish them from areas where enemies might appear.

It turns out that, when you have a map with one level of FOW, implementing a second level is incredibly easy. So I will pick up from where we left off at the end of the previous article. If you haven’t read it yet, it is essential that you do so, using the link at the beginning of this one if you like. To avoid repetition, I will assume that everything discussed in that article is understood.

You can find all the code for this article in its GitHub repository.

Starting from that map, we need to consider that the shader we used for the map (Assets/Shaders/Map.gdshader) checked each pixel of the map’s image to see if that same pixel was colored in the mask map rendered by SubViewportMask. If it wasn’t colored on the mask map, it assumed that the pixel was not visible and painted it black instead of painting it with the color coming from the map’s SubViewport (remember that the map's TextureRect was connected to the image rendered by SubViewportMap). Conceptually, the mask map corresponds to the areas already explored.

In this new map, we want to take it a step further by distinguishing the explored areas (the mask map) from the directly visible areas. The former will be shown in a dimmer tone, displaying only the static part, while the latter will continue to be rendered as before.

What are the directly visible areas? Those are on the shapes map. They are rendered in SubViewportShapes with a camera that only captures the white circle placed on the character. So far, we had used the shapes map for the mask shader (Assets/Shaders/MapMask.gdshader), but now we will also use it in the map shader to know which pixels of the map are in the player’s visible area.

Once we know how to distinguish, within the explored areas, which ones are in the visible zones and which ones are not, we need to render an image that only shows the visible part of the map. As in the other cases, this can be achieved with a SubViewport. In my case, I simply copied SubViewportMap and its child camera. I called the copy SubViewportStatic.

SubViewportStatic

To ensure that this SubViewport only shows the static part, the Cull Mask of its camera needs to be configured to capture only layer 1, where I placed all the static elements of the environment.

Screenshot of the camera inspector

Note that the same camera in SubViewportMap is configured to capture layers 1 and 4 (1 for the static objects in the environment and 4 for the character's identifying icons placed on them).

To make the image captured by the camera dimmer, you need to assign it an Environment resource (in the field with the same name). Once you’ve created this resource, you can click on it to configure it. In the Adjustments section, I enabled it and lowered the default brightness to half.

Environment configuration of the camera

Notice that the Environment resource has many more sections, so imagine the number of things you could do to the image captured by the camera.

With that, we now have a dimmer image of the environment, but without characters. Just the static part we needed. The map shader will receive this image through a Uniform (Static Map Texture), which we’ll configure through the inspector to point to SubViewportStatic.

Map shader inspector


Under the hood, the shader code is very similar to that of the previous article.

New shader code

The main novelty is the new uniform we mentioned earlier (line 12) to receive the image of the static elements map, and lines 16 to 18. If the code reaches this point, it means that line 14 concluded that since the pixel was marked with color in the mask map, it corresponds to an already explored area. In line 16, it checks if that pixel, besides being in an explored area, corresponds to a colored pixel in the shapes map (i.e., the directly visible areas). If not, the pixel receives the color of the equivalent pixel in the static map image (line 17). If the pixel was indeed colored in the shapes map (and its red channel was different from 0), that pixel would receive the default color coming from SubViewportMap (which shows the enemy icons).

The result is the image that opens this article, with the area surrounding the character showing the closest enemies, the more distant explored areas displaying only the static elements of the environment, and the unexplored areas colored in black.