Making a RTS game #2: Adding a very basic UI (Unity/C#)

Today, we continue our journey in programming a RTS (real-time strategy) game with Unity…

This article is also available on Medium.

In the first article of this series, I introduced the project and focused on implementing one of the core mechanics in any RTS game: placing buildings on the ground while checking for invalid placement, collisions, etc. We saw that, in a more advanced version of the game, we would probably want some sort of “building menu” that would list all possible building types, and that would let us click on a button to select the building we want to place.

So to start this UI off gently, we are going to create a very basic interface with the building menu containing one button per building type. Styling is going to be crude but it will implement all required features to list the different types of buildings, pick the one we want to create and then link this to the buildings placement system we programmed in the previous tutorial.

Adding a new building type

In order to actually have a choice in our building menu, and display more than one button, let’s first add a new building type in our global list. We will update the code from last time and stop using fuzzy names like “Building”; instead, I decided to go for “House” and “Tower”:

In my prefabs folder (the one located in the “Resources” folder we created previously), I need to rename the prefab that already exists to “House”. Then, I’ll create another one, also based on a cube, but I will stretch that cube on the Y-axis so it is taller, and I will name the prefab “Tower”:

Perfect, I now have two types of buildings I can pick in my building menu!

Creating the building menu

How do you add a user interface (UI) to a Unity scene?

In Unity, UI relies on the use of a Canvas. It is a special type of game object in a scene that lets you display graphic elements in a “foreground layer” on your screen, so to speak. Basically, your camera renders all of the 3D stuff moving around in your scene, and the Canvas comes to superimpose 2D UI elements above the rendered image. This way, you can get on your screen your game scene with buildings, characters, doodads and whatnot; and then on top of it the menus, info bars, system controls and other purely “extra diegetic” things.

When you want to render UI objects, they all need to be children of this initial Canvas game object. Unity offers plenty of elements to create more or less complex interfaces: panels to group several UI elements together, image displays, simple labels or even inputs and checkboxes.

For our RTS game, we need a panel – the building menu – containing as many buttons as there are building types. Because we want these buttons to depend on our BUILDING_DATA list (defined in the Globals.cs file), we want them to be instantiated dynamically rather than added manually beforehand. We could create the buttons by hand in the edit mode and rematch them at runtime, but there is a big risk that we forget to update the UI if we change the data structure… so it’s best to let the code create the UI elements when it starts!

Setting up the “BuildingMenu” game object

Our building menu is going to be a bar on the right of the screen with buttons in a vertical layout. First, create a new Panel object from the Game Object > UI menu in the top bar.

Then set its anchor point and position to be stuck to the right of the screen and take the full height:

In order to control the size of the buttons and give them a square size, we will use a GridLayout component. And we will make the background a semi-transparent black color so that the UI is not too eye-catching:

At this point, when we start the game, we have a pretty empty scene with a black bar on the right of the screen: that’s our building menu! It’s time to populate this menu with our list building types.

Preparing the UIManager script and the button prefab

The first things we need to do is to create a new class, the UIManager. This class will be in charge of handling all UI-related actions: creating buttons, updating the text of a label, getting the value from an input… It is the central hub that interacts with the UI elements; it gets or sends data to the rest of the scripts but doesn’t actually modify the state of the game, only the state of the interface. Anyway, since it’s global to all the UI in the scene, it makes sense to add it to our “GAME” game object, the empty game object we added in our scene to handle all global management.

In this script, we use some public variables (buildingMenu and buildingButtonPrefab) so that they are exposed in Unity’s inspector:

  • buildingMenu holds a reference a UI game object that is a child of the Canvas and will be the parent of all the building buttons
  • buildingButtonPrefab is linked to a prefab we are going to create of a UI button customized for the building menu

Reminder: in C#, by default, variables are private. This means that if I declare a variable with: int a;, then it’s actually equivalent to private int a;. Unity does not show private variables in the inspector, but it shows the public variables. To see those variables, simply click on the game object that has the script you want attached on it and go to the script in the inspector panel. There will be some fields you can fill or drag things on to create links.

To create the UI button prefab, the process is similar to what we did in the previous tutorial, except that we first create a new UI button from the Game Object > UI > Button menu in the top bar. Also, we should create a “UI” subfolder in the “Resources/Prefabs” directory to create this new prefab in.

Then, we can select the “GAME” game object. In the inspector panel, in the section dedicated to its UIManager component, we see two fields corresponding to our public variables. We can click and drag the “BuildingMenu” game object from the scene hierarchy in the first slot and the prefab in the second slot.

Preparing the BuildingPlacer script

Before we can create the buttons, we need to do a little modification to the BuildingPlacer script we started last time and added to our “GAME” game object. Remember that so far we are automatically picking the first building type in the Start() method of this script and we haven’t implemented a way to select another type.

So, let’s update the script as follows:

  • We remove the Start() method because we don’t need to pre-select a building type anymore
  • And we add a SelectPlacedBuilding() function we can pass a building type index to to have a new “phantom” building (of the right type) ready-to-place sticked to our mouse

Looping through our building types to instantiate buttons

Now that we have our SelectPlacedBuilding() method, we can loop through all of the types in our global list and instantiate the newly create button prefab as many times as necessary. For each button, we will update its text to be the reference of the building type, make it a child of the buildingMenu transform and attach the proper callback function to it.

The script looks like this:

Most of it is quite straightforward… but you might surprise by the _AddBuildingButtonListener() function. Why did I extract this from the loop?

This is actually an interesting pitfall of callbacks in loops related to the notion of variable scoping. The first time I tried it, I did the natural thing and defined the callback directly in the loop:

But when I ran the code, I got an index error. And by debugging the value of i passed to the SelectPlacedBuilding() function, I discovered it was 2. So, the variable actually had the value it had at the end of the loop and not the one it had during the iteration I defined the callback. Why? Because the variable i is declared in the for loop and therefore is linked to this scope. When you use it in the button’s callback function, you don’t capture its particular value at this loop iteration but a reference to it that will keep on updating as the loop continues.

When you extract the callback definition, you create another function with its own scope; and in this scope, i really has the value you think it has at this loop iteration! This trick seems a bit convoluted at first but in fact it is due to an important and essential feature of programming languages: variable declarations and scopes.

We are now able to click on the buttons in the building menu to pick a building type, have a “phantom” building, move it around, check for invalid placement and stick it on the ground! Yay! 🙂

What’s that building doing on the ground, over there?

If you watch the video carefully, you might notice that when we click the button, we are actually leaving a building on the ground, on the right, when we click to switch to the “Tower” building type. That’s because we need to cancel the placement of buildings on mouse click if we are hovering a UI element.

To fix this, we simply need to use Unity’s EventSystems package that takes care of the interactions with UI elements. We will just check that we don’t currently have an event for a UI element, like this:

And tadaa! We now have a fully-functional (although basic) UI to select a building type and place one or more buildings on the ground afterwards.

Conclusion

This article was a quick peek at Unity’s UI system. As we go through the tutorials, we will gradually refine the interface and add in new buttons, labels or displays to give more info to the player about the game. The next tutorial will pick up where we left off and add in some in-game resources (gold, wood, stone…) that the player will eventually be able to collect and to use to build those buildings.

Leave a Reply

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