Let’s keep working on our RTS – today, we’ll see how to change the building construction process to use worker units!
This article is also available on Medium.
So far, we’ve been placing buildings directly and instantly on the ground, as “all-mighty god-like players”. It’s pretty neat and some strategy games work like this (again, the Industry Giant series is a good example), but most RTS are a bit different: instead, you have a builder/worker unit that can construct buildings over a short period of time.
Today and in the next tutorials, we’re going to refactor our building placement scheme to incorporate this logic. In this first part, we’ll focus on implementing the core logic; then, in the upcoming weeks, we’ll improve the visual and take care of various refactors.
Important note: the technique we’re using now is valid too so, in truth, you could stick with this for your RTS. Up to you to decide if you want to keep this, or follow these episodes and go more for a Warcraft 3/Starcraft 2 vibe where you have builder units! 🙂
An overview of the construction logic
Before we dive into the implementation, we need to go over the logic to see what refactors this change implies – as we’ll see, it will require a lot of little changes in various places of the codebase…
The core idea is to replace our current basic placement with the following process:
- our building units can produce character units
- some of these character units can, in turn, “instantiate buildings” (thanks to a new type of skill: “INSTANTIATE_BUILDING”)
- when a unit “instantiates a building”, it places it on the ground where you left-click but the building is not yet alive – for now, it is in an intermediary “to-be-built” state; this means it doesn’t produce resources and you can’t access its skills
- the building units can then go near the placed building and, as long as they are close enough, they will be in “building mode” and gradually increase the “construction ratio” of the building
- when the building reaches a construction ratio of 1, it’s finished building and is set to active: it can now produce resources, you can access its skills, etc.
This notion of construction ratio is a temporary thing that we won’t keep in the final game — it’s just that it makes it easier to understand and implement our process for now. This is a common technique in game dev: whenever you can, try and cut down your feature in small chunks that you can code up iteratively so that you can regularly test it works the way you planned it 😉
Note that placing buildings will still be instantaneous – it’s just that placed buildings will not yet be “alive” and really functional; we’ll have an additional construction phase to finish them up.
As you can see, this will imply quite a lot of modifications in our code, but also in our UI. For example, the building menu will disappear (since placing building units will now be triggered by a skill available on some units), we’ll need to show more info on these skills to replace the “lost info” on building cost, and much more!
On the C# side, we’ll obviously need to re-update our
SkillData class to handle this new “INSTANTIATE_BUILDING” type, our
Unit-derived classes, the unit managers and even the
CharacterBT to get this new “construct a building” task in the behaviour.
Ok – that’s a lot to cover, so let’s get to it! 😉
Adding the new skill type
Updating the data class
The first thing we clearly need to add is the new skill type, “INSTANTIATE_BUILDING”. This is similar to the “INSTANTIATE_CHARACTER” type except that it doesn’t directly produce a unit that’s kicking and alive… Rather, it will replace the logic we had so far when we clicked on our building buttons: it will call the
BuildingPlacer and put us in “building placement” mode.
Let’s go ahead and add this to the
These skills are pretty basic to cast, but they do require that we define a new singleton in our project, on the
BuildingPlacer class, as well as some new prototypes to feed in the type of building to “prepare” for placement (instead of the data index):
Preparing the new skills and units
Now that we’ve improved our data classes, let’s prepare some resources for this new construction process. What we’ll do in this tutorial is prepare a basic production workflow that goes like this: first, the “House” produces a “Worker”; then, a “Worker” can build new “House” buildings, and so on…
For this, we’ll need a couple of things:
- our new character unit, the “Worker”
- two new skills: one to produce a “Worker” and one to build a “House”
- the “House” building unit will need to have the new skill “Produce a Worker”
- and the “Worker” character unit will need to have the new skill “Build a House”
For the Worker unit prefab, I’ve simply duplicated my Soldier unit and replaced the mesh with a slightly deformed sphere.
Then, I duplicated and updated the Scriptable Object a bit to get matching info:
Note: I haven’t prepared any sounds yet for this unit, so I will get some warning in my scripts when I try to play null clips, but nothing too bad 😉
The skill “Produce a Worker” is again pretty similar to “Produce a Soldier”:
On the other hand, “Build a House” is of the new type “INSTANTIATE_BUILDING” and it has a zero-time cast – indeed, we want the building to be placed instantly, it’s only after placing it that we’ll need some time to construct it 😉
Don’t forget to assign these new skills to our House and Worker units:
At this point, if you run your game, you will have a new way of placing a House: instead of using the building menu, you can create a Worker and then have it “instantiate” a House.
Yeah, that sounds a bit useless at the moment, I know. But we’ve actually already put in place an important part of our new construction process… 😉
Introducing the construction ratio
At this point, what we’d like is to implement this temporary notion of “construction ratio” for our buildings: rather than instantly getting an “alive” and functional building, our
BuildingPlacer should create a building in a “to-be-built” state that has a construction ratio of 0.
Then, only when this ratio reaches 1 will the building actually be alive.
The construction phases
This means that we’ll need to decompose our building creation in several phases.
- first, when we place it, the building is “fixed” and “active”, but not “alive”. In this state, the building has a construction ratio somewhere between 0 and 1. It is selectable (because we want to be able to examine the current progress of the construction) but you don’t have any info on the unit, you can’t use its skills and its behaviour tree is disabled.
- then, as a Worker works on the building, the unit will see its construction ratio increase more and more
- finally, when this ratio reaches 1, the building will set itself as active: it will compute its production efficiency, enable its behaviour tree and be fully accessible to the player
- when it becomes active, the building will also play the “building placed” global sound, enable its own ambient audio source (if it has one) and update the navigation mesh
To do all of this, we have to change the
Building classes. First, in the
Unit class, we’ll introduce a new accessor to check whether or not a unit is “alive”. We will say this is true by default and then override it in a second for the building units that will compute this flag a little differently than the character units:
Now, in our
UIManager, we’ll only want to show the info on the selected unit if it’s really “alive”:
Finally, let’s handle the different creation phases in our
This is mostly a translation in C# of the logic I described above – hopefully, it’s pretty self-explanatory 🙂
You see that I only enable and play the audio source in the
_SetAlive() util method and that I also moved some lines from the
Let’s also take this opportunity to do a quick fix in our resource display updates: for now, we are only triggering a UI update when we place a building, and not when we “place” (i.e. produce) a character. To avoid this issue, let’s take the “UpdateResourceTexts” event triggering from this same
_PlaceBuilding() method and move it to the
Place() method of the
Unit script that is shared both by characters and buildings:
The last thing we need to take care of is, in the
BuildingPlacer, when we force-spawn a building (and therefore bypass the “normal” construction process), we need to set the construction ratio to 1 immediately. Otherwise, the initial building will be in the intermediate “to-be-built” state and unusable!
If you run your game again, you’ll see that, now, when you place a building, it is in the intermediary state with only limited info, and it doesn’t produce any resource (we still get only +7 gold per turn thanks to our first auto-built House):
Showing the construction ratio in the healthbar
To tell the player where the construction is at for a specific building, let’s use our unit healthbar and, if we have a building in a “to-be-built” state, we’ll show its construction ratio instead of the health ratio in this bar.
First, let’s go to our
UnitManager and make the
UpdateHealthbar() method a protected virtual one so we can have a different logic for building units (same thing for the
healthbar variable, we’ll turn it into a protected one):
Important note: for the sake of brevity, I’ve omitted the other functions in this class here but, since we’re renaming
UpdateHealthbar(), be sure to properly refactor those names in the rest of the file, too 😉
Now, in the
BuildingManager, we’ll override this method and, if we’re currently “active” but not “alive”, we’ll show the construction ratio. Else, we stick with the old code and we show the current health over max health ratio:
We can even add a little command to our debug console to easily test the result with different construction ratios on our selected building unit:
Now, if I try and set varying construction ratios, you see that my healthbar reflects this value. Also, if I go over 1, I jump into the “active” state and get back a normal behaviour for the building unit:
Note: of course, for now, I don’t have any logic to auto-update the healthbar yet so I need to re-select the unit to see the change 😉
Adding the “build” action to characters units
Time to finally link our Worker unit to this construction process!
Modifying the data class
First, we’ll need to add some new parameters to our character units for this build process. Just like with the attack, we’re going to have a
buildRange and a
buildPower will determine how much the builder adds to the construction ratio of the building every time it runs a new build cycle: we’ll simply divide this integer value by 10 to get a ratio-compatible increment.
Note: again, this will be different in the final game and we’ll see in a couple of episodes how to have building units require a specific “amount of construction” and how to make character units define their own “build power” 🙂
But also, because these values won’t change as the unit upgrades, we can store them in the
CharacterData class directly instead of making an (editable) copy in the
We can now set these values for the Worker unit, in the Scriptable Object:
Updating the character behaviour tree
Getting the target
Now, we want the Worker unit that initially prepared the building to go towards it and start the construction. In other words, we want to communicate the placed building as the new target to the character behaviour tree of this unit.
To do this, we’ll first store the Worker in our
BuildingPlacer script when we call it from the “INSTANTIATE_BUILDING” skill:
Then, we’ll add a new public method to the
CharacterBT class that re-uses our
SetFormationTargetOffset() function from the last tutorial to set the building as the current target (with no offset), and call it from the
BuildingPlacer in the
Note: having an external source “enforce” the behaviour of the builder unit is, to a certain extent, a breach of the behaviour tree paradigm; but here, it makes it really easier to code and, in my opinion, pretty readable… 😉
We also need to add this new optional transform parameter to the
SetFormationTargetOffset() method so that it can assign the target if it’s passed directly like this:
Using our new target in a “build” task
The “Build” task will be pretty close to the attack one – basically, while we want to attack an enemy target unit, we want to build a friendly target unit if we can build and if the target is not already finished building.
Also, in terms of priority, we’ll add our new build branch just after the attack sequence (remember that priority in these behaviour tree diagrams reads from left to right). This means that, like before, the right-click destination/target supersede the follow sequences; and then, inside these follow sequences, we first try to attack if the target is an enemy, or else try to build if the target is a friend, or eventually try to get closer to the target if it’s not impossible to perform the task at hand from our current position:
Something important to note, too, is that we are going to replace our “EnemyInAttackRange” check node by a plain “UnitInRange” check node that can be used both for enemy or friend units and that takes in a little boolean flag to either use the attack or the build range as threshold:
Be sure to refactor and/or replace it properly in the
BuildingBT classes, and to pass in the
true flag in our current instances of this node that check for enemies with the attack range! 🙂
For example, in the
BuildingBT script, we now have:
Then, our new “Build” task node will be similar to the “Attack” task node – we just get a reference to the current target, retrieve the
BuildingManager component on it and call a new method on it,
This function is quite short and easy to implement, so let’s add it right now in our
BuildingManager to get rid of the compilation error 🙂
As you can see, I also return a boolean to tell whether the building is finished constructing or not. This way, I directly know if I should clean up the target from my behaviour tree data slots and exit the “build” mode:
Finally, we can update our character behaviour tree to follow our new diagram:
At this point, we’ve successfully linked our Worker to the construction process and we see that it now moves towards the building, stops nearby and increases its construction ratio gradually until it reaches 1 and the building is “alive”:
The nice thing is that, thanks to our previous coding of the behaviour tree and unit formations, we can even have multiple Workers go to construct the same building, so that it finishes faster!
Changing our UI
Cleaning up the building menu
Now that we can create buildings using our Workers, we can actually remove the building menu from our UI! So, instead of having both the building buttons by default and the selected unit panel when we have one or more unit selected, we can just have the selected unit panel.
First, in the 3D scene, it means that we can rename the building menu something like “SidebarBackground” and make the background of the selected unit panel transparent:
This way, we either have just the background (no unit selected) or the background plus the selected unit panel elements on top of it (one or more units selected).
Then, in our
UIManager script, we can clean up the variables and pieces of the logic that referenced the building menu, initialised it or updated it:
This means that there are also some prefabs and events that we can clean up in our project!
- firstly, we don’t need the UI “BuildingButton” prefab anymore:
- secondly, we can get rid of our
BuildingButton.csC# script; more precisely, we will convert it to a
- thirdly, we can remove the triggering of now unused events – so, in our
BuildingPlacerclass, let’s drop the line with the “CheckBuildingButtons” event in the
Note: of course, feel free to add a “Build a Tower” skill and give it to the Worker to replace the building button for this unit, too 🙂
Showing up info on the skills
However, because we’ve removed lots of things, we also need to re-incorporate some info in our UI to keep the player up-to-date. In particular, at this point, we have made players completely blind to the cost of units: both characters and buildings are produced via skills, but hovering the skill button doesn’t display any info panel!
We’re actually going to do something pretty similar to our
BuildingButton – let’s rename the class (and the file)
SkillButton and trigger a new set of hover/unhover events:
Remember to add this component to the “UnitSkillButton” prefab:
And finally, let’s use those new events in our
If we run the game again, we now have a little info panel whenever we hover the skills to tell us what the skill does and how much it costs (if it instantiates units) 🙂
Today, we’ve seen how to refactor our construction system to have actual units walk towards the buildings and take time for their construction. The core logic is in place, but there are still various things we could do to improve the visual of this new construction process.
Also, remember that this is a more common feature of RTS games than the “instant placing” we had before, but it is just one idea among others and you can also stick with the previous way if you prefer 😉
Anyway — next time, we’ll continue working on this feature by having the building shape change throughout its construction, adding some smoke effect and finally have a limited number of workers for one given construction.