This article is also available on Medium.
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.
So today, let’s see how to add this core feature to our RTS game! And just to give you an idea, here is a preview of the selection system we will have by the end of this tutorial:
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 😉
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 behaviours 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 behaviour through the base class but they can each override 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
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).
UnitManager parent class
In our case, we will quickly want to share behaviour 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 that is a MonoBehaviour and inherit from it, and that’s it! 🙂
Important note: because
BuildingManager inherits from
UnitManager, and it extends its functionalities, you should NOT have both a
UnitManager and a
BuildingManager on your units: just by having a
BuildingManager, you already have all the behaviour of a
UnitManager too! TL;DR: just keep the
BuildingManager on your units and DON’T add any
UnitManager, that would degrade performance and make for inconsistent behaviours! 😉
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 by 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 coloured 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
Secondly, we’ll use those new functions in a
My old Warcraft 3-vibe lead me to pick a greenish colour for everything selection-related. So, I used a vibrant green and a transparent green for the inside and the border of the dragging box, using Jeff’s functions in our
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
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.
Deselect() functions to the
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
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
OnMouseDown() Unity built-in function to check if the mouse is hovering a unit when 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
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.GetMouseButtonUp(0) in the
BuildingPlacer script (in the
To implement the “alive” flag in our
UnitManager and have a specific behaviour 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 “specialise” interfaces or behaviours in your derived classes. That’s thanks to the idea of abstract, virtual 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 behaviour. But sometimes this common behaviour 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 behaviour. This override can either completely replace and ignore the initial virtual method to create a unique behaviour, or call the parent’s virtual method as part of its logic to extend while still using the shared behaviour.
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:
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
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:
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 behaviour 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.