This post is also available on Medium.
In this tutorial, we are going to reorganize some of our classes to better prepare what’s to come: we will create a clear logical hierarchy for our units that can be either buildings or characters. We started that process in a previous episode of this series, but we’re not done yet.
At that point, you might realize that we could – and should! – re-use the notions of inheritance and polymorphism we saw in the previous tutorial for our data and unit classes. What we want is to replicate the kind of class hierarchy we have between
BuildingManager at two levels:
- data – the
BuildingDataclass should inherit from a
- instance – the
Buildingclass should inherit from a
Proxying for the data classes (
We need to have a
UnitData class that our
BuildingData class can inherit from. This parent class will need to centralize all the fields that are generic to all units like, say: the unique code/name, the description, the number of initial health points, and the cost in game resources.
Wait, isn’t that our entire
BuildingData class? Yes it is! That’ s because so far we haven’t introduced any particularity that makes it a building rather than a character…
Let’s make the best of this situation and tackle the problem while it’s a quick-win. ‘Cause it’s going to be quick:
- rename the
UnitData, do the same for the files themselves (rename
UnitData.cs) and update the Scriptable Object associated menu:
- create a new
BuildingData.csC# file with a super-basic class that just inherits from
- change the building Scriptable Object associated menu
And that’s enough for the data classes! 🙂
What we did here is a “direct transfer”, a proxy that sets the scene for future coding but does not inherently add functionality.
An important note: we do not want to make a global list of building and character types – it is better to keep a separate list of building types in our
Globals class. So we don’t need to change our data loading mechanism at the moment.
Also, we should update two small pieces of code in our current scripts: firstly, in our
CustomEventData, we can replace the
BuildingData-typed field by a
This will allow us to pass in data on any (sub)type of unit in our events. Similarly in the
UIManager, in our
_SetInfoPanel() function, we can use our new parent data class and replace the field accesses:
Separating the instance classes (
Creating the same sort of hierarchy between the
Building classes is going to be a bit more complicated because we have to extract the proper fields and methods but keep some behavior that is specific to the
Building in the derived class. Basically: the placement mode, the materials switching and the
DataIndex getter are only useful for buildings so we can take them out. The rest should remain in the base
Re-updating the unit managers (
Finally, we need to change a few things in our manager classes to properly use the class hierarchy we just created:
- since it is used by all types of units, we are going to move the box collider requirement and assignment to the
UnitManager– so it needs to become protected instead of private
- we need a reference to our unit instance (a
Buildingvariable): because we use some specific behavior from the
Buildingclass in the
BuildingManager, we can’t benefit from polymorphism and wait for run-time “mutation” this time; instead, we are going to override a getter to convert the value to the right type
- we’ll move the
Initialize()method to the
UnitManagerso it is common to all unit types
This gives us the following bits of code:
A good opportunity to prepare some future features
This little refactor is also a nice opportunity to think ahead and prepare data that will be useful in the future. There are three things I want to add to my instance classes: a unique instance uid, a unit level and a unit resources production Dictionary.
- Having a unique uid (or “uid” for short) is always interesting because it’s a sure way of differentiating between different instances of the same thing; C# has a built-in
Guidthat directly gives us the tool for that
- Units have levels because I will put in place a system of upgrades: the player will be able to level up buildings and characters in order to improve their stats (and optionally produce new units in buildings).
- Resource production is very similar to resource cost, that’s why we’ll use a List of
ResourceValuein both cases; the resource production Dictionary simply maps an in-game resource unique code to the amount that’s produced by that unit each “turn” – I haven’t decided how often the resources are collected yet, so I’m voluntarily vague on this topic… 😉
Both those properties are linked to an instance and not a data class, because two instances of a Tower could have different levels, and two instances of a House could have different productions, for example. And they are possibly valid for buildings and characters alike, so I’ll put them in the
Unit base class. They are pretty straightforward to implement:
You’ll notice that I’ve added another constructor so that if no production is provided when creating a
new Unit(...), the program is able to automatically define an empty production Dictionary on its own. For good measure, it’s better to do the same on the derived
The point here is not to dive into the whole resource collection system – this will be a topic for another tutorial. But at least at this point, everything is ready for us to implement it!
This tutorial was shorter than usual but it was important to take a step back, look at our classes and plan what is to come. Having introduced this new hierarchy and the few additional fields, we’re making our lives easier for the upcoming features.
Our refactor will already pay off greatly in our next episode: we will have a second go at our units selection system and code up selection groups, integrate a “selected unit” panel and list the currently selected units.