Making a RTS game #37: Using workers to construct buildings 2/3 (Unity/C#)

Let’s continue our RTS and improve the visual of our refactored construction process!

This article is also available on Medium.

Last time, we started refactoring our building construction process so that, instead of placing them instantly on the ground, we have Worker character units build it gradually, over a short period of time.

The core logic is in place, but there are still some improvements we can make to the visual of this construction process.

So, today, let’s continue this feature by changing the building shape throughout its construction, adding some smoke effect and finally limiting the number of Workers for one given construction. Then, next time, we’ll wrap up our new construction scheme by adding some sound and finishing up the refactors.

Modifying the building visual as it builds!

It is pretty common in RTS to have the visual of the building change throughout construction. This is quite useful as it allows the player to quickly get an idea of how advanced the construction is at a glance, without necessarily selecting the building unit to check out the exact construction ratio.

Now, once again, I am no game artist – so I will be fleshing out the code to have this logic in your game, and I’m sharing some example meshes in the Github repo, but those are clearly not prod-level 😉

All in all, the idea is pretty basic: we are going to define a list of meshes in our BuildingData classes so that all instances of a given building unit type can retrieve it and iterate through it as their construction ratio increases.

This means that, in our BuildingData, we can add the following:

And, for example, for my House, I can drag in a few additional FBX imports I made of my model at earlier stages of the construction:

Be careful: the order of the models in this list determines which one will appear through the construction – the first one will be the one at the very beginning, before the construction starts, and the last one will be the finished building visual.

Finally, in the Building class, we want to get back this list of meshes as well as a reference to our renderer (that’s located on our “Mesh” sub-child game object), and then pick meshes from this list whenever we set the construction ratio:

By using this formula, we make sure that we get updates regularly but that the “final mesh” (the last in the list) only appears when we reach a ratio of 1.

Here’s the result in-game!

A fix in passing… computing better distances!

Before we actually work on the construction system, let’s refactor a bit our distance computations in the CheckUnitInRange and TaskFollow nodes.

Rather than using the mesh scale, it would be better to use the mesh bounds to get an absolute size that is scale-agnostic. Now that we have various meshes, this can help with having better stop distances.

We’ll simply get the Renderer component of the “Mesh” child game object and look at its bounds.size; but to optimise the computation a little, we’ll cache this result while the target stays the same:

While we’re at it, let’s also do the same thing we did in the CheckUnitInRange class for the TaskFollow one and check whether we should look at the attack or the build range!

Ok – that’s a bit better and it should help us work with our new meshes 😉

Making the unit “enter” the building during construction

Quite often, in RTS games like Warcraft 3 or Starcraft 2 for example, you don’t actually see the Worker near the building it’s currently taking care of. Instead, as soon as they start the construction, they sort of “disappear” inside it and only re-appear when it’s done.

This logic can be implemented pretty quickly in our character behaviour tree, in the TaskBuild node class, just by checking and toggling the unit renderer on or off.

First, let’s create some util method in our CharacterManager to handle this renderer switch. I’ll toggle both the Renderer and the BoxCollider components:

I’ve exposed the characterRenderer and characterCollider as public variables so, as usual, I need to drag them in the Inspector window. With my current prefabs, these variables are components on either my parent game object, or the “Mesh” sub-child game object inside it:

Now, I’ll want to use this new SetRendererVisibility() method in my character behaviour tree nodes, namely TaskFollow and TaskBuild.

First of, in the TaskFollow: in a previous episode, we already checked to see if the target unit is ours or not; so if it’s ours and we’re building, we’ll toggle the renderer off when we reach the destination.

Then, in our TaskBuild script, if we’ve finished the construction, we’ll toggle the renderer back on:

If you run the game, you’ll see that, now, the Worker “disappears” when it reaches the “to-be-built” construction site and reappears when the building is finished 🙂

Limiting the number of builders

So far, we’ve allowed an unlimited number of Workers to come and participate in the construction. This is a valid system, of course, but in lots of strategy games, we rather limit this number to just 3 or 5. This way, you don’t have a massive movement of units, you don’t finish the construction instantaneously and Worker still have an individual purpose!

This is the sort of things that you should try and tweak so that it matches and runs smoothly with the unit upgrade system we implemented a few weeks ago – for example, if you want players to actually use the upgrades, you should ensure that a squad of dozens of Workers isn’t better than a handful of well-upgraded ones…

So – in my game, I’ll limit the number of Workers per construction to 3. Then, if all 3 slots are full and another Worker comes to help it will stop next to the building and won’t participate in the construction (so it will simply revert to its idle state after reaching the target construction site).

To do this, let’s first add a little list of references to CharacterManager instances, _constructors (along with some public methods to modify it and an accessor) to our Building class:

And a little boolean flag in our CharacterManager to make sure we don’t set a Worker to be a constructor multiple times:

Then, when a Worker unit reaches a “to-be-built” construction site and tries to enter the “build” mode, it will check to see if all slots are taken. If there are, it will simply clear its target data and reset to an idle state. Else, it will do several things:

  1. it will add itself to the _constructors list of the building
  2. it will set its own _isConstructor flag on
  3. it will warp at random around the building: this is to make sure that the unit is close enough for the behaviour tree to properly reach this branch, but to avoid collisions when the renderers will re-appear
  4. it will switch to the “Build” task

Note: we also automatically deselect the unit when it “enters” the construction site 🙂

Don’t forget we need to reset this _isConstructor flag when the construction finishes, in the TaskBuild class!

If you run the game, you’ll only get to assign up to 3 workers to your construction site; the choice is basically done on a first come first served basis, but again Unity’s navigation computation can sometimes create some unexpected deltas… 😉

And as a little bonus: let’s use this _isConstructor boolean to disable selecting of the Worker units when they’re already engaged in a construction (since they are “inside” the building and busy)!

We just need to go to our CharacterManager script and override the IsActive() method to check that we’re not currently in “build” mode:

And, in the UnitManager, move the check on the IsActive() to the _SelectUtil() so that it works for all selection types (box selection included), and not just the direct left-click single select:

You’ll see that we’re now not able to select the busy constructors anymore!

Showing up the current number of builders in the UI and removing workers

In the first part of this tutorial, we cleaned up our UI and we made sure to hide our selected unit info on the “to-be-built” construction site until it’s finished.

Today, we’re going to change this a little and create a new UI panel that displays the 3 build slots, either filled or empty, so that the players can easily check if they could add more Workers to this task or not.

I’ll also make these slots little buttons so that, when I click them, the unit in this slot is removed from this task and returns to an idle state, next to the building; this will allow me to assign other (better) units to this construction, or to get back this unit for a more important construction!

First, we can prepare a basic panel with just a vertical layout and three children, one for each slot. Inside each child, I added a button with a simple sprite of a hammer I made (you can get it in the Github repo 🚀):

Now, in our UIManager, we’ll reference this panel and toggle it on if we select a building in its construction phase. When we show the panel, we check how many constructors the building currently has to properly refresh the UI:

We can also link this to a new event so that, if the building is currently selected, the UI automatically refreshes whenever a new constructor comes in or leaves:

Finally, let’s see how to make those buttons “remove” constructors from the construction. First, let’s link the button to a callback function:

Note: remember that when assigning a button callback in a for-loop, you should extract this process to its own function to avoid scope variable issues – I talked about it a long time ago, in the second episode of this series ! 😉

And now, in our Building class, let’s finish up this function. We’ll want the Worker unit to exit the building, restore its renderer and clean its target to return to an “idle state”. We are once again going to expose a method on our behaviour tree (even if it’s not completely in sync with the BT philosophy, it really makes things easier…), and call it from the RemoveConstructor() function:

There is a little corner case you might spot if you run the game and try to remove a unit from a building construction, and then re-add it immediately: the renderer won’t toggle off the second time! That’s because, for now, this logic is only run in the TaskFollow class, so if the Worker is initially too far from the construction site to build it directly.

To fix this, we can just copy this logic in the CheckUnitInRange script, too, like this:

You can now see the number of Workers in your construction site as they come in and out, and you can remove some from the construction by clicking on those buttons:

Adding some smoke VFX

Another real nice addition to our construction progress would be to have some smoke effects on the construction site while it’s building.

Now, there are various ways to create visual effects. Quite often, for example, you can go for particle systems. However, these are pretty heavy in terms of computation, and they really shine only when looked at from up-close…

In our case, particle systems would be “too much”, they’d be overkill. Given our camera and RTS-view, we’ll be looking at the smoke effects from quite a distance. So we can make something a bit more optimised and, instead, rely on sprite sheet animations.

Creating smoke animations

We already discussed this idea a couple of tutorials ago, when we created our player banner in the UI: remember that we created an animation filled with sprites that can be played to get a basic movie from this sequence of images.

Here, we’ll do the same and have small sprites with smoke effects pop up on the construction site to indicate its currently in progress.

You can of course make your smoke sprites yourself – but if you’re like me and you can’t really work good with a pen, you can also use some CC0 resources like this amazing ensemble of free smoke/explosion VFX effects by the Unity team. Here, I went ahead and took the four “Wispy Smoke” flipbooks.

After downloading and unzipping the flipbooks, you should get .tga images containing an 8×8 grid of still images that, all combined, make an animation:

Simply copy this .tga sprite sheet to your assets and make sure to turn it into a Sprite, in Multiple Sprite mode:

Note: you can also reduce the Max Size property from 2048 to a lower resolution, for example 512, to reduce the memory consumption of this asset in your final game – again, since we’ll see it from far away we don’t need that much detail!

And, to wrap this up, we want to divide this single sprite sheet into a sequence of images. Luckily, this can be done automatically in Unity by using the Sprite Editor.

In more recent versions of Unity, you’ll need to import the “2D Sprite” package from the Package Manager to have this Sprite Editor available:

Once it’s installed just click the Sprite Editor button and you’ll get a new popup window that shows you your single sprite sheet. Then, click on the “Slice” button at the top and choose “Grid By Cell Count” with R = 8, C = 8 (8 rows, 8 columns). Finally, click the “Slice” button at the bottom:

As soon as you’ve clicked on “Slice” you’ll see lines appear on the sprite sheet to show the cells and your asset will contain a series of 64 sprites:

At this point, we can easily create an animation, just like in the aforementioned tutorial with the player UI banner.

The workflow is exactly the same: create a GameObject > 2D Object > Sprite in your scene, select it and open the Animation window; then, create a new Animation Clip and drag the sequence of images into the timeline to auto-stack them as a movie.

I decided to put this Sprite inside an empty game object so that I could get more control on the scale of this Sprite Renderer:

Creating the Animation Clip via this process will automatically create an Animator component on your game object, too. You can inspect it and optionally modify the speed of the Animation Clip in the animator state, if you want (I myself put it at 0.7).

I repeated the same workflow for the other 3 smoke flipbooks to get 4 smoke prefabs:

And now that we have those prefabs, we are ready to use and instantiate them in our scene! 🙂

Using object pooling for VFX instantiation

Ok so – the overall logic we want to implement here is that:

  • when we start the construction of a building and the first constructor is added to the site, we’ll spawn some smoke VFX in the area to indicate the construction is in progress
  • if we remove all constructors, we’ll clean up those VFX so that we see the construction has stopped
  • if the construction finishes and the building comes “alive”, we also need to remove the smoke VFX

This is another little trick to give the player a better intuition of where the construction is at – just by looking at the visual on the ground, you can see if there is a Worker currently on this site or if its on hold at the moment.

At first glance, you might think that we’ll just instantiate and destroy smoke prefabs on our construction sites. And, it’s true: we could do that. But this is a very unoptimised approach.

It is way better to use the object pooling pattern.

What is object pooling?

Something that is very important whenever you do some game dev – or even programming in general – is that, overall, creating and destroying objects is costly. Continuously re-instantiating and deleting stuff means that you’re doing a lot of memory allocations, deallocations and re-allocations which is always inefficient. This is mostly due to caching and the way that computers sort objects in memory: they always prefer to have objects in neatly consecutive chunks of memory, but destroying an object means deleting a chunk, which leaves a hole – this is the problem of memory fragmentation:

And so then, you either have to account for this empty space afterwards, or move everything that is further down this memory line to remove the hole… neither of which is quick or free to do.

It’s usually way more optimised to keep the same objects as much as possible, and simply hide and show them, or move them around in your scene to fake the apparition of new ones. This way you don’t create holes in your memory lines: once an object has reserved a spot, it keeps it until the end of the process and doesn’t collide with the rest of the objects in memory. So the object pool pattern relies on defining some objects as “obsolete” and “resetting” them from time to time, but never re-creating new ones or destroying them completely. We just simulate their appearance or disappearance via various tricks, but the memory manager is happy because we ask for one big chunk of continuous memory at the very beginning and then that’s it: you keep working with the same objects in memory over and over again.

Note: you can have an object pool that is larger than the number of objects shown initially: your routine may only reveal some after a while. But the whole point of that pattern is that the pool should have as many spots as the maximal number of objects needed in your scene at the same time.

Applying this pattern to our VFX spawning

Chances are that, in a real game (and perhaps even in the rest of this tutorial series), we’ll have more than just smoke VFX. So to handle all of our VFX easily and in a centralised fashion, let’s create a new C# script, VFXManager.

This class will prepare some object pools for the various visual effects in the game; for example, we’ll have one object pool for our smoke effects.

An object pool will be a basic hierarchy of empty anchors with two groups: the “Stock” and the “InUse”:

Be careful about which game object is active or not in this hierarchy: you want to toggle the “Stock” child off and the “InUse” child on – this way, the pooled objects that are in the “Stock” will be hidden and the ones in the “InUse” will be visible.

Ok – now that we have this prefab ready, let’s get to coding!

We’ll place this script on a new empty game object in our scene, “VFX”, that will be the main parent for all VFX object pools:

The VFXManager will have a Singleton reference, and some references to our “Pool” and smoke effects prefabs. In its Start() method, the VFXManager will prepare the various pools for the various VFX types.

For now, we’ll just have the “Smoke” type, but we’ll set up an enum to have an adaptable system that can easily allow for other VFX (fire, magic particles…).

To setup the object pools, we’ll instantiate a new “Pool” prefab and pre-instantiate a given number of objects inside its “Stock” anchor. These objects will be chosen at random from a list of prefabs – in our case the smoke prefabs.

So, here is the VFXManager at this point:

The value for the SMOKE_EFFECT_POOL_SIZE variable is of course up to you — the larger this number, the more smoke VFX you’ll allow at the same time on screen… but the heavier it will be in terms of memory and initial instantiation! 😉

Also, don’t forget to drag in the prefabs in the component slots:

Then, we’ll Spawn() or Unspawn() VFX objects from the pools – the idea is to always take the first child (i.e. the “oldest” one) and either move it from the “Stock” to the “InUse” group, or place it as the last (i.e. the “more recent”) child in the “InUse” group.

Note that we have two prototypes for the Unspawn() function: we either want to remove a specific VFX object, or we just assume that we should unspawn the oldest one. This will allow us to remove the exact smoke effects for one building but it also lets call the object pool in a more agnostic way, if needed 🙂

Calling the VFXManager from our Building class

Finally, in our Building script, we’ll update the AddConstructor(), RemoveConstructor() and _SetAlive() methods to spawn or unspawn smoke VFX.

These VFX will be positioned randomly on the construction site using our Poisson algorithm – but since the navigation mesh is carved at this position (don’t forget we want our buildings to be obstacles for the navigation agents), we can’t project the position on the nav mesh; so we won’t be using our SamplePositions() util method but instead the SampleOffsets().

We’ll store the smoke VFX so we can remove them later on with our second Unspawn() prototype, like this:

We now have little smoke VFX that “pop and disappear” when a constructor enters/leaves a “to-be-built” construction site, or when the building comes “alive”:

Of course, thanks to this generic system, you can easily add other VFX, like sparks or debris, if you want 🙂

Conclusion

In the last two tutorials, we’ve started to refactor our building construction process so that it uses builder units instead of instant placement. We’ve also added visual effects to make this more appealing to the player, and we’ve made sure to limit the number of constructors.

But there are still a few final touches that remain and that we’ll finish up next time, like adding sound effects or replacing the construction ratio with construction variables that are more on a per-unit type basis…

Leave a Reply

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