Making a RTS game #4: Selecting units (Unity/C#)

On with the coding of our RTS game! Today: the selection of units…

This article is also available on Medium.

Disclaimer: This episode of the series is very inspired by Jeff Zimmer’s tutorial on RTS-like selection in Unity 5. In particular, util functions are simply copy-pasted from his code, the mouse dragging box logic is almost identical and I used his “projector” selection circles. There are however a few differences in the way I store selected units and activate/deactivate the selection circles and additional concepts due to the already existing components of our RTS project 😉

When you’re playing a strategy game, one of the key points is to be able to interact with the things on screen. Most notably, you want to be able to select the buildings and characters on the ground in order to give them orders or get additional info on their current state. This selection logic usually relies on two types of actions for the player: either drag a box around the things you want to select, or click on some of them more specifically.

Enough talking – here is a preview of the selection system we will have by the end of this tutorial:

So let’s dive in and add this core feature to our RTS game!

Introducing “units” into our code: inheritance and polymorphism

In RTS games, there are oftentimes different “units”; this generic term encompasses both characters and buildings – they are the aforementioned “things” on the screen you want to interact with as a player. While characters can move around and perform actions, buildings are fixed on the ground and usually used to collect resources or create new character units. However, both have some common features – most notably, the ability to select a unit (character or building) in order to get more info on it and optionally call some of its actions/creation mechanisms.

In other words, both “characters” and “buildings” can be thought of as special cases of “units”. In programming, this brings us to the notion of inheritance and polymorphism.

Inheritance, polymorphism: quésaco?

Quoting the Microsoft C# docs:

Inheritance, together with encapsulation and polymorphism, is one of the three primary characteristics of object-oriented programming.

The whole idea of inheritance is to re-use, extend and specify interfaces or behaviors from a base class in derived classes. Roughly speaking, you have a basic blueprint (the base class) and you can then define other blueprints adapted to different use cases (the derived classes). All your derived classes share behavior through the base class but they can each modify and extend it in specific ways.

The typical example is to have an Animal base class that defines a MakeSound() method; and then to create the Cat and Dog derived classes that adapt the MakeSound() to either print “miaou” or “woof”.

Polymorphism builds on this concept to allow classes to “mutate” types at run-time: you can declare an array or a list of items with the base class as type and later fill it with items of either the base class type or one of the derived classes type.

Disclaimer: this is a very (very!) light overview of huge OOP concepts so if you’re not familiar with them, feel free to dive into more in-depth references (for example for inheritance, the Wikipedia page or the Microsoft docs are good starting points).

Defining the UnitManager parent class

In our case, we will quickly want to share behavior between all of our units: buildings and characters alike. Also, some features should be agnostic of the specific unit subtype (typically the selection mechanism). Hence the need for polymorphism!

Alright, then let’s create this UnitManager class so that our already existing BuildingManager can inherit from it. For now, the UnitManager doesn’t need to do anything – it’s just an empty shell. But very soon we will put the unit selection logic in it… and by inheritance, all of our buildings will automatically gain this feature too!

So… just create a new C# file called UnitManager.cs and that’s it! 🙂

Adding a “unit” tag

Remember how, in the first tutorial, we used a “Terrain” tag to distinguish the terrain object from the others and ignore collisions with it? We’re going to use tags once again, this time to easily list all units in the game scene. Just add a new “Unit” tag in the list and apply it to our two House and Tower prefabs.

Preparing the selection circle object

There are several ways to create a selection circle around a unit in our 3D scene:

  • we could create an actual 3D object, a circle around the mesh
  • or use Unity’s LineRenderer component
  • or use Projectors

I followed Jeff tutorial and went for the last solution that uses projectors. I think the result in Jeff article is really nice so I just copied its assets to my project – we have a shader, an image and a material. The image is used as a cookie for the projector, and the shader is used for the material:

I can then add an empty game object to my House prefab and add a Projector component to it that uses my assets. Note that the Orthographic Size of the Projector depends on the size of my building – it’s roughly equal to the width/depth of the mesh but you can adjust it manually so it’s more pleasing to the eye!

The last important thing to do is to actually deactivate this game object in the prefab: we don’t want our units to be selected when they are first instantiated in the scene!

Getting and displaying the mouse dragging box

In a previous episode of this series, we saw that Unity provides an intuitive UI system based on the Canvas element. But there is an “older” method that was available before this Canvas system was put in place (before Unity 5): using the OnGUI() method to directly draw colored pixels on the screen in the right place. As explained in the docs, this function is available in any MonoBehaviour class and it is called automatically by Unity’s engine during the game update loop. Inside of this function, you can use Unity’s GUI package to draw lines, rects, polygons, texts, etc.

Firstly, let’s create a new Utils.cs C# file where we define a static class containing various utilities – those are directly taken from Jeff’s tutorial:

I won’t explain all of the code because I believe it isn’t too hard to understand if you’re a bit familiar with Unity and C#. But I do want to point that, at the top of the script, we see another example of the Singleton pattern for the WhiteTexture variable. The rest of the functions show good examples of Unity’s camera space transformations (going from the 3D world to the 2D screen and/or viewport requires you to change your reference and this is done via built-in methods like Camera.main.ScreenToViewportPoint()).

Secondly, we’ll use those new functions in a UnitsSelection class:

My old Warcraft 3-vibe lead me to pick a greenish color for everything selection related. So, I use a vibrant green and a transparent green for the inside and the border of the dragging box, using Zimmer’s functions in our OnGUI() hook.

We simply add this UnitsSelection script to our “GAME” object, the manager in the scene. And at this point, we can drag boxes on the screen when we click and drag the mouse!

Selecting units in the box or when clicking on them

Creating a global list of selected units

Pretty basic with our current setting, it’s like always – just a new static variable in the Globals class!

Remember however that advanced containers like Lists or Dictionaries need to be initialized with an empty structure before you can add items to them. So the new List<UnitManager>() part here is necessary to avoid errors when running the code.

Adding the Select() and Deselect() functions to the UnitManager

Let’s finally add some code to our UnitManager – we’ll add a public variable to hold a reference to our selection circle and two methods to select and deselect the unit:

This script is basically updating the global list of selected units and activating/deactivating the selection circle object. Of course, you’ll have to set the public variable in the inspector by opening the House and Tower prefabs and dragging the SelectionCircle child object of each into the slot that appeared in their BuildingManager component.

Selecting the units inside the box

Now, whenever we’re dragging, we can use the GetViewportBounds() utility to check if a unit is within the selection box dragged on the screen. To list all possible units, we’ll use the “unit” tag we created previously. Units inside should be selected, and units outside of it should be deselected:

This works great – we’re selecting units with our screen selection box, youpi!

Selecting a unit when we click on it

Another common feature in RTS games is the ability to select units when we click on them. To do that, we can just use the OnMouseEnter() and OnMouseExit() Unity built-in functions to check if the mouse is hovering a unit, and then select it if we left-click. We just have to be careful to clear the selection on a single click so the rest of the units are deselected – this is done using an override for our Select() function:

Note: when we clear the selection, we require a copy of the list (here, selectedUnits) in order to iterate through the original and remove items one by one. If we take the list directly, we will just have a reference and the code will throw an error saying it can’t iterate on a list that is changing count at the same time.

This allows us to select units on a single click while deselecting the rest 🙂

But the problem is that when we place the building on the ground, since we’re also left-clicking to do this action, we’re immediately selecting the unit. This is because we need to make 2 fixes in the code:

  • we need to use the mouse button “up” event for building placement and the mouse button “down” event for selection to avoid conflicting actions
  • we also need to wait for the unit to be “alive” before enabling selection; for buildings, this means waiting for their placement mode to be “fixed”.

The first point is easy enough to tackle: we simply replace Input.GetMouseButtonDown(0) with Input.GetMouseButtonUp(0) in the BuildingPlacer script (in the Update() method).

To implement the “alive” flag in our UnitManager and have a specific behavior in the BuildingManager, we will use virtual and override methods. We saw that in C# and OOP programming more generally (at least the way it’s done nowadays…), the idea of inheritance is very important. Two consequences of this approach are:

  • the scoping of variables: I already mentioned it in a previous tutorial but here I’m referring to the level of access you have for variables inside a class instance when you are outside the class, inside the class, or inside a child class. In typed OOP-oriented languages like C#, this is represented by respectively the public, private and protected access modifiers.
  • the sharing and extension of methods: as I’ve said before, a benefit of inheritance is that you can share but also “modify” or “specialize” interfaces or behaviors in your derived classes. That’s thanks to the idea of abstractvirtual and override methods. Roughly speaking, an abstract method lets you define a shared interface – your parent class doesn’t have any logic inside the function and the method cannot be run on an instance of this parent class, it’s just there to enforce the presence of the method on the class and derived instances, and the inputs and outputs formatting. A virtual method on the other hand can have some logic in the parent class, so it may be used to share behavior. But sometimes this common behavior does not correspond to the specific case of the derived class, and so this child class should implement an override of the virtual method to provide its specific behavior. This override can either completely replace and ignore the initial virtual method to create a unique behavior, or call the parent’s virtual method as part of its logic to extend while still using the shared behavior.

Here is this technique applied to our two scripts. The UnitManager first defines an IsActive() protected function to compute if the unit is considered “alive” or not and sets it to true by default. Then, the BuildingManager overrides this logic and instead uses its own Building instance’s placement state to check for the “aliveness” of this specific unit subtype:

Deselecting units

Finally, let’s program the deselect logic. We want the selection to be cleared if we press the <Escape> key or if we click on the ground. The latter is done using a raycast, like we did for the building placement system but this time we don’t check only for the “Terrain” layer, otherwise it will just ignore the objects on the way. Rather, we will cast a ray and match all objects, but check for the “Terrain” tag for our deselection process:

Bonus: Updating the current selection with the <Shift> key

This is also a classic of RTS games: when you have one or more units currently selected and you left-click another unselected one while holding the shift key, it adds this unit to the selection. On the other hand, if you left-click a selected unit while holding the shift key, it deselects it. It is quite quick to add, although we have to think of some corner cases. This requires a bit of refactoring of the UnitManager class.

The following code renames the clearSelection flag, updates all the default values in the override and adds a little util function called _SelectUtil() to avoid code duplication:

Conclusion

And we’re done! We now have a pretty neat selection system 😉

In this tutorial, we introduced the generic notion of “unit”. We then made the BuildingManager class inherit from the UnitManager class to extend its behavior and allow for polymorphism. And after those preparations, we implemented the selection mechanism by using a projector for the selection circle, some screen-to-world transforms for dragging the selection box and some collisions check or raycasting for more advanced selections.

As the project continues to grow and we keep on adding more buildings, characters, resources and other type of data, we may start to struggle with static variables defined by hand in our Globals.cs file. To make this data easier to read and update, next time we will see how to use Unity’s Scriptable Objects to store some of our data.

Leave a Reply

Your email address will not be published. Required fields are marked *