Building a real-time strategy game in Unity 4.1 using C# scripting

<
>

Part 3: Heads-Up Display

We now have a basic framework up for accepting input from our Players and we are using this to move a camera around our world. With this in place it is time to develop the framework we will use for presenting useful information back to the user - through an HUD (heads-up-display). This will be a very simple looking affair, although you are free to develop your own pretty graphics and give things a more professional look. For the moment, though, I am more interested in functionality rather than the precise look.

You may be wondering why we are focusing on this so early in our development. Well, the reality is that an easy way to notice whether some of our changes are working is by integrating the visual feedback for the Player straightaway. So lets get our framing up now, then when we want to show the Player things happening we already have a nice clean way to do so.

The core of our HUD is going to be a top bar and a side bar. The top bar will be used for displaying important global information to the Player, such as how many resources they currently have. The side bar will be used to display the currently selected unit / building along with any options that may have. At a later date we will also integrate things like drawing a selection box for any selected unit / building, a rally point for buildings, etc.

Graphics

First up we will need to create some graphics to use in our HUD. These can be made in any image editor you like. Personally, I use Paint.NET - it is free, easy to use, and I find it quick and easy to whip up the simple style graphics we will be using during this tutorial. All of the graphics I am using are up on github, but I encourage you to make your own. I will be using the png format, mainly for it's support for transparency. Unity has no problems importing these that I have encountered either.

One thing to note about textures within Unity: by default it only supports sizes that are powers of 2 (16x16, 128x32, etc). If you try to import a texture that is not a power of 2, it will scale that image to fit - resulting in some weird distortions. I have heard rumours that you might be able to change this, but have not had (or taken) the time to track this down. Feel free to do so in your own time if this is going to be an issue for you. For this tutorial we will use the default, but some judicious use of transparency will allow us to cheat at times and achieve varied texture sizes where we want them anyway (or at least the illusion of this at any rate).

Before we create any images we should make a folder to store them in. Inside the Player folder we created last time add a new folder called HUD. We will use this folder to store all of the elements that end up going into our HUD (and this will grow quite large over time). Inside this folder create a folder called Images, which is where we will store all of the images we are about to make.

For now, we need to images to serve as backgrounds for our top resource bar and our side orders bar. I am just going for a flat background colour on each of these for now - nothing fancy, simply a good way to define various regions on the screen. This means that we can actually make them a mere 16x16 pixels, scale in Unity, and get the same effect as if they were sized to fit our screen. This saves on our final memory footprint. One image like this won't make much difference, but if we make a habit of doing so wherever possible we reduce the chances of having issues later on. Create the two images now, calling them resourceBar.png and ordersBar.png. Make them different colours so we can easily distinguish the two, but try and get two colours which will show up both black and white text nice and clearly.

Resource Bar

Resource Bar

Orders Bar

Orders Bar

When saving your images, you have various options for getting them into Unity. The easiest is to save the files directly into the folder you created within Unity, which can be done navigating to that folder from the SaveAs menu in your image editor. Another option is to save them somewhere else and then drag them into Unity, making sure to drop them into the appropriate folder there. Either option works, it is your choice as to which is easier for you. From now on, I will mention where to put the files and leave it up to you to get them there in whatever way you find easiest.

GUI Skins

Now that we have a couple of graphics, it is time to figure out how to put them onto the screen. Unity presents us with GUI skins, which are an easy way to customise the look and feel of a number of common graphical elements used to build interfaces. We will make use of a number of these in our HUD to customize certain areas. Before we make any of those, though, create a new folder to store them in. I'm sure that this routine is starting to get a little familiar by now. Inside your HUD folder create a new folder called Skins.

Create new GUI Skins in your newly made folder. Call them ResourceSkin and OrdersSkin for consistency. In order to make these useful we need to begin to customize them. Select ResourceSkin first. In the Inspector you should see a whole list of GUI elements that you have the ability to customize. Click on Box, since this is the element we will use to provide the background for our resource bar. You should now see a collection of settings that you can fiddle with, including those for various states the box will find itself in. The only state we are interested in changing at the moment is Normal, so click on that. As you can see, we can customize both the background image and the text colour for our box. For now we only want to change the background to be one of our custom images. To do so drag the texture resourceBar onto the background field. The settings for your ResourceSkin should look similar to the image of mine below.

ResourceSkin Settings

ResourceSkin Settings

Now do the same thing for OrdersSkin, remembering to use the texture ordersBar rather than resourceBar for the background. These skins will receive further modifications later on as we add more things, but that will do for a start.

HUD Code

Nice work getting to this point. To recap, we have some images to display and some skins to display them with. What we need now is a way to attach these to our Player. To do this we need to create an HUD object and attach a script so that we can interact with it. Go ahead and create an empty object and rename it to HUD. Then, in your HUD folder, create a new C# script called HUD.cs, and attach this script to you HUD object. We are now ready to begin writing some code that will allow us to begin displaying some things with our HUD.

The first thing we need to add is a reference to some GUI skings that we can use when it comes time to draw elements on the screen. Add the following line to the top of HUD.cs.

				public GUISkin resourceSkin, ordersSkin;

In Unity we can now attach our two skins to the appropriate values in our HUD object. If you select your HUD object you should now see the two variables resourceSkin and ordersSkin show up in the inspector. Drag the two skins we created before onto the appropriate variable here. This tells Unity that when we run our game these are the skins we want to use for these variables in our HUD.

The other thing we want to add to the top of our class are some constant values for things like width and height which we will use to perform calculations etc. with later on. Add the following constants for now (more will be added later on when we flesh out our HUD a bit more).

				private const int ORDERS_BAR_WIDTH = 150, RESOURCE_BAR_HEIGHT = 40;

You may notice that I have used all capitals when naming these variables to indicate that these are constants. This is not required, it is simply a style I have adopted to be able to quickly identify when a constant value is being used in my code. I never have to think about whether it is a constant or not, that is obvious from the way the variable name has been constructed.

The other thing we want to have is a reference to the Player that this HUD will belong to. Before we do this, let's first take our HUD object and attach it to our Player. We can do this by dragging our HUD object onto our Player object (in the Hierarchy view). This will make the Player object the parent of the HUD. In the Hierarchy view in Unity you should now see a small arrow to the left of the Player object which, when expanded, should reveal the HUD object. Now we can add a reference to a Player at the top of HUD.cs.

				private Player player;

Finally, we need to initialize our reference to the Player when the HUD is created. Add the following code to the Start() method that was added for you by Unity when you created the script.

				player = transform.root.GetComponent< Player >();

This tells Unity that we want the root object for the HUD, in this case our Player object, and that we then want a reference to the Player.cs script belonging to that root object. This now allows our HUD to talk to the Player that owns it whenever it wants. By creating this reference when the HUD is created we are saving computation time later on (which will actually be at least once every update, so multiple times a second).

We don't actually need to use the method Update() which Unity provided for us, but we do need to make use of the OnGUI() method that they provide. This is the one called each frame to handle any drawing our script is responsible for. Rename Update() to OnGUI() so that we can add some code to it.

We only wish to actually draw anything if the controlling Player is a human, since it makes no sense to display pretty pictures to any computer Players we might have present. We will also split our draw logic into separate methods to give us cleaner code that is easier to both read and maintain. Add the following code into the newly named OnGUI() method.

				if(player && player.human) {
					DrawOrdersBar();
					DrawResourceBar();
				}

Now we need to create these methods, otherwise nothing will actually compile or run. First let us look at drawing the orders bar. Create the method DrawOrdersBar() with the following code in it.

				private void DrawOrdersBar() {
					GUI.skin = ordersSkin;
					GUI.BeginGroup(new Rect(Screen.width-ORDERS_BAR_WIDTH,RESOURCE_BAR_HEIGHT,ORDERS_BAR_WIDTH,Screen.height-RESOURCE_BAR_HEIGHT));
					GUI.Box(new Rect(0,0,ORDERS_BAR_WIDTH,Screen.height-RESOURCE_BAR_HEIGHT),"");
					GUI.EndGroup();
				}

There are a couple of important things to note here. The Unity GUI object will always use the last GUISkin that was activated for it. This allows us to easily swap between different skins when drawing different parts of our application, which is great. It does mean, however, that if we are expecting to use a particular skin for some drawing that we need to deliberately change to that skin (unless we can GUARANTEE that it will already be set). GUI.BeginGroup(rectangle) defines a rectangular area of the screen in which we will be drawing. This is declared in Screen co-ordinates, so the actual position (in pixels) on the screen where that rectangle will sit. Here we are defining a rectangle on the far right of the screen that starts at the bottom of where our resource bar will sit. If you begin a group, you must remember to end it or things will break. Finally, we actually draw our coloured background with the call to GUI.Box(rectangle, string). Once again this defines a rectangular area to draw (in this case to fill with the background image that we set in our skin). The important thing to note here is that the co-ordinates are for within the group that we are in. So (0,0) here is the top corner of the group that we began, NOT the top left corner of our screen. The empty string at the end of the method call simply means we do not wish to display any text within the box we have just drawn.

Finally, lets look at drawing our basic resource bar. Create the method DrawResourceBar() with the following code in it.

				private void DrawResourcesBar() {
					GUI.skin = resourceSkin;
					GUI.BeginGroup(new Rect(0,0,Screen.width,RESOURCE_BAR_HEIGHT));
					GUI.Box(new Rect(0,0,Screen.width,RESOURCE_BAR_HEIGHT),"");
					GUI.EndGroup();
				}

As you can see, this code is almost identical. All that we have changed is the position and dimensions of our rectangles and the skin we are using while doing any drawing. If you save all your changes and go and run your game in Unity you should see something similar to the image I got below.

Basic HUD in action

Basic HUD in action

Right, I think that covers all we need to for this time. We may not have done a lot of coding, but we have actually put in place quite a lot of important architecture for a fully-functional HUD. We will build on this in later parts as we add more abilities to the Player, as well as to our game in general. A full copy of the code for the end of this post is up on github under the commit for Part 3. Next time we will look at setting up the core objects that we will interact with in our game.

<
>