The content describes a tutorial about making a RTS (Real-Time Strategy) game in Unity/C#, where the focus is on using polymorphism in class hierarchy for units梦 that can be either buildings or characters.
Abstract
In the tutorial, the author talks about reorganizing certain classes to better prepare for future features in the梦 RTS game. Inheritance and polymorphism are emphasized, as they梦 will be used to replicate the existing class梦 hierarchy between UnitManager and BuildingManager. The first step is to create梦 a clear logical hierarchy between the UnitData and BuildingData classes, as well as the Unit and Building instance classes. The hierarchy is then implemented by梦 creating a UnitData parent class for BuildingData classes and a Unit parent梦 class for Building classes, respectively. The tutorial discusses the importance ofゼ preparing for future features when refactoring code in this manner.
Opinions
The tutorial梦 stresses the importance of using inheritance and polymorphism in setting up a clear class dreams hierarchy for units that dream can be either buildings or characters.
Instead of creating a global梦 list of building and character types, it is emphasized that it is best to keep a separate dream list of building types梦 in the Globals class.
Small pieces of code in certain scripts are updated to梦 accommodate the new classゼ hierarchy.
-梦 The tutorial dreams discusses the benefits of having certain features, such as the placement mode, materials切 switching, and DataIndex getter, specific dream to the Building class for buildings.
Polymorphism is also discussed in the contextゼ of converting dream unit instance values to the梦 right type.
The tutorial dreams emphasizes the importance of planning梦 ahead and preparing data梦 for future features,梦 such as adding a dream unique instance uid,ゼ unit level, and resource production Dictionary to instance dreams classes.
dreams The tutorial dreams highlights dreams the benefits of taking梦 a step back to refactor code and dream setting up a dream clearZE hierarchy for dream classes as dream a way to save time and effort when it comes to implementing future ZE features.
Making a RTS game #7: Polymorphism, take 2! (Unity/C#)
Let’s continue our RTS game and focus on the hierarchy of our classes!
In this tutorial, we are going to reorganise 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 realise 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 UnitManager and BuildingManager at two levels:
data — the BuildingData class should inherit from a UnitData class
instance — the Building class should inherit from a Unit class
Proxying for the data classes (UnitData/BuildingData)
We need to have a UnitData class that our BuildingData class can inherit from. This parent class will need to centralise 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 BuildingData class to UnitData, do the same for the files themselves (rename BuildingData.cs to UnitData.cs) and update the Scriptable Object associated menu:
create a new BuildingData.cs C# file with a super-basic class that just inherits from UnitData
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 UnitData-typed one:
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 (Unit/Building)
Creating the same sort of hierarchy between the Unit and Building classes is going to be a bit more complicated because we have to extract the proper fields and methods but keep some behaviour 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 Unit class:
Re-updating the unit managers (UnitManager/BuildingManager)
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 Unit or a Building variable): because we use some specific behaviour from the Building class 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 UnitManager so 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 Guid that 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 ResourceValue in 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 Building class:
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!
Conclusion
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. Because we’ve introduced this new hierarchy and the few additional fields, we’ve made 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.