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 the required features to list the different types of buildings, pick the one we want to create and then link this to the building 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, there are several ways to create UI. A very common choice however is the Unity UI Game-Object-based toolkit, that relies a lot 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.
UIManager script and the button prefab
The first thing 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 it’s not meant to actually modify the state of the game, only the state of the interface.
But 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 (
buildingButtonPrefab) so that they are exposed in Unity’s inspector:
buildingMenuholds a reference a UI game object that is a child of the Canvas and will be the parent of all the building buttons
buildingButtonPrefabis linked to a prefab we are going to create of a UI button customised 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 to 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.
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.
Anyway – 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.
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.