Making a RTS game #21: Adding players and unit ownership (Unity/C#)

Today, let’s add some players and an ownership system to our RTS!

This article is also available on Medium.

The last tutorials have been about game parameters and UI displays of our settings – we’ve been working on the metadata of our game quite a lot. But it’s been a while since we haven’t added a quick-win feature: something quick to implement but essential to the game.

In this episode, we’re going to see how to create a list of players for the current game, and how to associate units to players through an ownership system.

Prelude: simplifying our GameParametersEditor (a lot!)

In recent episodes, we’ve seen how to create our own custom Unity UI editor. We replaced the default Inspector for our GameParameters-derived classes assets with a tailored-made one. For the sake of this tutorial, I decided to go in deep and even talk about C# reflection. This allowed us to get familiar with manipulating dynamic data types and made it easier to introduce the logic for our in-game settings panel.

But! There are two important things to note with our current GameParametersEditor script:

  1. if we start to put in more “complex” data types and in particular collections, the Inspector will go haywire because we have no logic to handle arrays or lists
  2. Unity actually has some powerful utilities when it comes to creating custom editors… and those tools make for a much simpler code 🙂

For example, my whole “check for the field type and show the proper field” routine can really be done automatically by Unity by using the EditorGUILayout.PropertyField() method. So rather than having our long chain of if/else if, we can simply replace our entire script by this one:

Yes, this simple class works perfectly, it will do the same thing as before and in truth handle even more data types! 😉

The idea is that:

  • we manipulate our asset through a specific reference, serializedObject: this is a C# variable that is directly defined by Unity and points to the asset being edited in the Inspector; we can cast it down to the proper type just like we did before to do some reflection on it and automate our fields listing
  • so, we automate the listing of the fields on the asset in the same way as we did before…
  • … but instead of calling our own _DrawField() method, we cut to the chase and take advantage of Unity’s PropertyField(); the only custom addition is that we create a little row to push our toggle button (for in-game visibility) before the default field’s display
  • we still check for the [HideInInspector] attribute on the field: if it has this custom attribute then we simply skip this field and try to display the next one
  • at the beginning of the OnInspectorGUI() method, we call serializedObject.Update() to get the latests values for all of our properties on this asset; and at the end of the method, we call serializedObject.ApplyModifiedProperties() to make sure that all of our PropertyFields do track the changes and save them to the asset

At that point, we’ve managed to reduce the size of the class significantly, while actually improving its functionalities! Indeed if you try and add some array of a basic type (say an array of strings) to one of your game parameters assets, it will now display properly (as opposed to our previous code that just failed miserably to show collections).

This simplification is only possible for Unity Inspectors, so what we’ve learnt about type reflection and field representation automation is still necessary for the in-game displays; but at least for this in-editor setup, it’s going to lighten the load a lot!

Introducing the PlayerData class… and its custom representation

Now that we’ve taken care of our game parameters assets and their custom editors, let’s dive into the real topic of the day: creating players and linking units to them.

To represent a player, we are going to create a new data struct, PlayerData:

For now, a player will simply have a name and a colour.

Note: I’m using a struct rather than a class because the PlayerData is a small-sized set of data and it will mostly be used via contiguous arrays. For more details on picking a class or a struct, you can check another article I wrote about some C# tips and tricks 🙂

This data is not a Scriptable Object in itself – it is only a serialisable data class that will be part of a new game parameters Scriptable Object class, the GamePlayersParameters:

As you can see, at the moment, these parameters are pretty basic; we just have our list of players and our own player index for this game (so we can find our info in the list easily). Since we’ve fixed our custom data display, you can go back to your Unity editor, create a new GamePlayersParameters asset from the “Scriptable Object > Game Players Parameters” menu, and you’ll see that it has a basic integer field for our player index, and an editable array for the players list:

This visualisation is okay, but it can be improved! In particular, it takes up a lot of space to show just two fields – those could be grouped on a single line. To create this custom representation of a property inside of an asset, this time, we are going to make a custom property drawer.

Let’s create a new C# class in our “Editor” folder (remember it’s at the root of your Assets folder) called PlayerDataDrawer.cs, and add the following code to it:

The PropertyDrawer class is another Unity built-in we can derive from to directly replace the way serialisable data of a given type is shown in Inspectors. Its OnGUI() method is called regularly to repaint the visual representation, it receives all the necessary info and it can be overriden to extend or modify the display. My code is slightly re-adapted from Unity’s detailed tutorial on the topic of custom editors and property drawers so if you want to explore this subject, go and check it out 😉

And since Unity’s default collection representation is able to get the type of the data in the collection, it can fetch the proper drawer (either the default one or a custom override if there is one) and “paste” it into the collection itself in the custom Inspector, like this:

We now have a readable but compact visualisation of our players list, and we have all the data we need to represent the players in terms of game data.

Implementing a unit ownership system

To “attach” our units to a given player, we are going to reference the index of the owning player inside the Unit instance. We’ll set the index when we create the unit and then read it at various points, for example to update the mesh with the colour of the player (so we easily see who the unit belongs to), to enable or disable the FOV (depending on whether or not the unit is ours)…

Adding the owner upon unit creation

First things first, let’s update our Unit class constructor to accept a new parameter: the index of the player who owns it. We’ll also need to add this parameter to the constructors of the derived Building and Character class so it is properly propagated:

Of course, now, Unity will start complaining about compilation errors – we have to update our code wherever we create units to add this new parameter!

To begin with, let’s add a new public variable to our GameManager to have access to our GamePlayersParameters asset; don’t forget to drag it in the Inspector, too! 🙂

Now, we can access those parameters from all of our scripts via the GameManager singleton instance, just by calling: GameManager.instance.gamePlayersParameters.

Most of the fixes we need to do are in the BuildingPlacer class. Here, what we need to do is give our own player index to the Building constructor so that, whenever we place a new building on the map, it is linked to us. This gives us the following modifications:

Then let’s take care of the other subtype of units we have, characters. For now, they are only produced via our “Produce Soldier” skill, so we need to update the SkillData class so the INSTANTIATE_CHARACTER-typed skills handle ownership. We simply need to get the skill caster (in our case, the building that produced the unit), get its “UnitManager” component and extract the owner of this unit from it. Then we assign this owner to the newly produced character unit, and we’re all set!

Displaying the player’s colour on the unit

When you work with basic meshes in Unity, you’re used to only having one material on your object. This material might be quite complex, with multiple textures and complex effects thanks to shaders… but they’re still only one material!

If you start to make your own game assets in 3D software like Maya, 3DSMax, Blender or anything of the sort, you’ll quickly see that 3D objects can actually have more than one material at a time. For example, if you make a cube, you can have the six sides have different materials so that you can customise the colours more easily.

To refer to each material on your object, we have material slots. Those are of course available in Unity too – if you take a look at the “MeshRenderer” component of any mesh in your scene (even one that has only one material), you’ll notice that there is actually a “Materials” variable that is an array. If you import your “multi-materials” 3D object, the list of material slots you had in your 3D soft will be shown in the “MeshRenderer”, and the material-to-geometry association you created before will be kept by Unity. In other words, the faces you linked to material n°1 in the 3D soft will be linked to the first material in the list of materials in your “MeshRenderer”, and so on for all the others.

In RTS games, a common technique for showing the unit’s owner is to have some part of the unit mesh display the player’s colour. For example, you may have a little coloured band at the top of your tower that takes on the proper colour, or a little flag next to your house. This is usually done via material slots: you simply create a new slot for this specific part of your mesh so that, later on, you can set it to a basic material tinted with the right player’s colour (while the rest of the mesh keeps its “normal” colour).

In our basic RTS example, we don’t have complex meshes – therefore we’ll have only one material slot. But we are still going to take that into account in our logic; to display the player’s colour on the mesh, we are going to upgrade our UnitManager script so that:

  • it holds a reference to the material slot corresponding to the “plain material” for the player’s colour
  • it has a new function, SetOwnerMaterial(int owner), that retrieves the material in his specific slot and tints it with the proper colour, according to the colour of the player matching the given owner index

Here is the updated script:

By default, we set the material slot to 0; this means that if there is only one material on the mesh, the entire mesh will be coloured.

Note: when you use the .materials property of a “MeshRenderer”, you cannot simply modify one of the items in-place. You need to reassign the entire array all at once for Unity to take the update into account.

All that’s left to do is to call it from our Unit constructor – this way, whenever we create a new unit, it will run this material logic and update the chosen material slot with the right player colour:

Let’s test it out! First, make sure you have defined at least one player in your GamePlayersParameters asset, and that you have a valid myPlayerId; here is my test setup:

Then, simply start the game. You’ll see that the building that is placed at the start of the game is now a cube coloured with your own player colour! And any new building that you place, or any new character unit that you produce will have the proper colour too:

Of course, you can easily change the colour in your players parameters asset, re-run the game, and see the modification be applied immediately on the units 🙂

Toggling the FOV depending on the unit’s owner

To facilitate the tests, we’re first going to refactor a bit our BuildingPlacer script – we’ll encapsulate the logic for spawning a building “programmatically” into its own method; this way, we’ll be able to easily fake either us or other players placing buildings on the map.

Here is the updated script:

As you can see, I’ve simply moved what we were doing in the Start() method into a new function, and I’m calling it with the proper parameters from the Start(). I only left out the call to _CancelPlacedBuilding() because we want to restore our previous state in case we were placing a building at that time. This way, I keep the exact same behaviour as before (my BuildingPlacer script still places an “initial building” at the start of the game in the middle of the screen) but it’s easier to spawn buildings to our liking.

We also need to make sure that there is no “build chaining”: if we spawn the building programmatically, we don’t want the script to let us keep placing a building of the same type if we have enough resources. So let’s add a little boolean to _PlaceBuilding() method:

And now, for example, we can try spawning a building for the other player in a corner of the map:

This is a bit rough, the building’s completely stuck to the corner which is not a good placement, but it doesn’t matter for our tests. The important thing is that doing this illustrates the issue we have currently with FOVs: they are enabled for all units, even if the ones we don’t own! So basically, we would be able to see the other player’s actions and units, just like ours. It really takes the fun out of the game…

You might also notice that:

  • we consumed the resources for building two Houses instead of one… even though the second House was built by the other player
  • and we can click on the House to select it!

The second point is something that we’ll eventually want so that we can also take a look at enemy units health points, for example. But for now, it would also give us access to the unit production skill, and possibly moving the units around… so that’s clearly not good! Until we improve this, we’ll just block this selection feature for units that we don’t own.

Luckily, all of this is quick to fix 😉

We simply need to do two things. First, let’s change the Place() method of our Unit class and add in a check for the unit’s owner against our player id:

Then, in our UnitManager, we’ll add a check for the owner of the unit before selecting it:

Tadaa! If you start the game again, the blue building in the corner won’t have its FOV enabled anymore (so it will be invisible in the dark), and your in-game resources will have only reduced by the price of one House instead of two. Also, you can try and click on it (blindly, I’ll admit) and see that it cannot be selected anymore.

Note: when you’re done testing, make sure to remove the additional SpawnBuilding() call in the BuildingPlacer Start() method 🙂

A little fix while we’re at it

I’d also like to take this opportunity to fix a little thing in our current unit instantiation process. For now, every time we create a new unit (either a character or a building), we first create the Unit-derived class instance and then link it to the corresponding UnitManager-derived script with the Initialize() method…

… but we’re not doing it in the Unit class itself!

Instead, we have to remember to call it after we’ve created our Unit-derived class instance. This is not ideal, and we should definitely move this logic to the constructor of the Unit class. So here is our modified Unit script:

Then, we need to update the BuildingPlacer script where we currently create our building units – we just have to remove the call to the Initialize() function that is now useless:

And similarly, we have to change the SkillData class where we create character units via the INSTANTIATE_CHARACTER-typed skills:

Conclusion

Today, we’ve significantly simplified the code of our custom editor for the game parameters assets, and we’ve made it more robust to complex data types thanks to Unity’s built-in editor UI tools. Also, we now have a list of players (each with a name and a colour) and we are able to associate units to players via our brand new ownership system.

Next time, we’ll continue working on our units and focus more on the buildings: we will see how to have them produce some resources depending on where they’re placed on the map…

4 thoughts on “Making a RTS game #21: Adding players and unit ownership (Unity/C#)”

  1. Hi,

    one of the best tutorials I ever saw. Thanks a lot! Two small questions:
    – will you include resources that can be mined by characters?
    – will you ever publish the complete source code?

    Pascal

    1. Hi! Really glad you like it – thanks so much for your nice comment 🙂
      – resource production is actually coming in the next tutorial (#22)! 😉 But it won’t be with characters moving around the map, rather it will be dependent on the location of the building, more in the style of management games like Industry Giant
      – I’m planning on publishing the entire source code when the tutorial is finished, but to be completely honest: I’m not exactly sure when the tutorial will end! I’ll continue making new episodes for as long as I have ideas… 😉
      It’ll probably linger for at least a few additional months!

      1. Hi Mina,

        glad to hear you have so many ideas to continue this series. I’m very curious to see how it goes from here. Then I will try to implement the resource mining myself.:-) Btw. you forgot the RTS tag on the last article. 😉

        1. Haha, nice catch! Thank you, I’ve fixed it 😉
          Yup, I’m also curious to see how long I can last… hopefully, I’ll keep on finding ideas and we’ll end up with an actual game!

Leave a Reply

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