Creating an RTS in Unity: Part III

Update: This can now also be found at stormtek.geek.nz/rts_tutorial/part3.php, where the entire tutorial is now being hosted.

First up, I apologize for the long delay between posts for anyone who has been following. Life got a little bit crazy and then I got distracted. I hope to keep things regular from now until the end.

We now have the basic framework up for accepting input from our players, and we are using this to move the camera around our world. Now 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 make it look a lot more professional. For the moment, I am more interested in the functionality than the precise look anyway.

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 some of our changes working is by integrating the visual feedback for the user straightaway. So lets get our framing up now, and then when we want to show the user things happening, we already have a nice clean way to do so.

The core of our HUD is going to be a top bar, where we will display current resources, and a side bar, which we will use to display the currently selected unit / building along with any options it has. At a later date we will also integrate things like a selection box for that 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 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 – also Unity has no problems importing these that I have encountered.

One thing to note about Unity with textures: by default it only supports sizes that are powers of 2 (16×16, 128×32, etc). If you try to import a texture that is not, it will scale it to fit – resulting in 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. 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.

Before we create any images, let’s 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 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 16×16 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 and ordersBar. 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.

When saving your images, you have two options for getting them into Unity. The easiest is to save the files directly into the folder you created within Unity, which means navigating to that folder from the SaveAs menu in your image editor. The other 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 see fit.

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, lets first 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.

Add two new GUI Skins to your newly created folder. Call them ResourceSkin and OrdersSkin for consistency. Now that we have some skins, it is time 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. The only thing we want to change is the background, 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 us for now.

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, also called HUD, and attach this script to you HUD object. We are now ready to begin writing some code to do things with our HUD.

The first thing we need to add is a reference to the skins we have created so that we can reference them 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. 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. 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;

Note the use of all capitals to indicate that these are constants. This is not required, it is just 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 like so:

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 now 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. Therefore, let’s 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.human) {
	DrawOrdersBar();
	DrawResourceBar();
}

Now we need to create these methods, otherwise nothing will actually run. First let us look at drawing the orders bar. Add the following code to DrawOrdersBar()

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 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. Add the following code to DrawResourceBar()

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 play your scene in Unity you should see something similar to the image I got below.

Basic HUD in action

Basic HUD in action

Right, I think this post has gone on long enough for today. We may not have done a lot of coding this time round, 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 posts as we add more abilities to the player, and 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.

Advertisements

35 thoughts on “Creating an RTS in Unity: Part III

  1. Chris says:

    I’m having an issue getting my colors to show up. I can’t see how the HUD.cs variables resourceSkin and ordersSkin are getting linked to my to GUISkin objects. The two GUI boxes show up correctly, but they’re just a grey with low opacity, instead of the colors from my two png files.

    Also, the code needs to be modified ever so slightly. Screen.Height needs to become Screen.height in both places it occurs.

    Thanks for this tut!

    • Hankey says:

      Got the same issue and don’t seem to find what’s wrong… Any idea ?

      Thanks for sharing your knowledge 🙂

      • Strange. I will have a look into this further … Probably won’t be for a few days though. To make sure I am in the same state as you I will start the tutorial from scratch and see if I encounter the same problems.

          • Tom says:

            Go to Hiearchy. Click on the HUD-Object under your Player-Object.
            In the inspector you see the HUD(Script)-Component. Here u find the 2 variables:
            resourceSkin, ordersSkin you created in the script.
            Just take your skins from the assetfolder and Drag-Drop them over this variables in the inspector.

          • Yea, if you have not attached the skins inside Unity then you will have issues making changes to them. I did not state how to do so in detail since I am assuming (as stated at the start of the tutorial) that you have at least done the very basic Unity tutorials, which cover things like that. I did at least state to attach the skins that you created to the HUD code inside Unity, which is done as Tom mentions.

  2. Chris says:

    Silly wordpress, I don’t know if my earlier comment was posted. How do the HUD.cs variables resourcesSkin and ordersSkin get linked to my two GUIskins? The HUD elements are showing up just fine, but don’t seem to be using my skin objects.

    Also, Screen.Height needs to be changed to Screen.height in the two places it occurs.

    Thanks for this great tut!

  3. in this part of code is Screen.height- and no Screen.Height-
    right?

    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();

    • That must have been a problem that slipped in when copying code into the blog sorry. I have fixed it in the post, it was fine in the actual code of github since I ran the code before committing it.

  4. CrashTest says:

    I think here is a mistake:

    player = transform.root.GetComponent();

    If you use GetComponent() method, you have to give type of component. Is should look like this:

    player = transform.root.GetComponent();

    • Thanks for that, I have fixed this in the post 🙂 I have it correct in my code (and thus on github) but obviously type this wrong here.

      The correct version is player = transform.root.GetComponent();

  5. short says:

    A little mistake with the call of DrawResourceBar, she doesn’t have the same name as in the declaration
    DrawResourceBar();
    private void DrawResourcesBar()

  6. Yeasy says:

    pls help me, i got a error in this line
    if(player.human) {
    DrawOrdersBar();
    DrawResourceBar();
    }
    NullReferenceException: Object reference not set to an instance of an object
    HUD.OnGUI () (at Assets/Player/HUD/HUD.cs:17)
    .:(

    • Is the HUD object attached to a Player object? It is breaking because the variable player inside HUD.cs is null. This will only happen if the line player = transform.root.GetComponent() in the Start() method is unable to find the Player it is meant to be attached to.

      To guarantee that the HUD code does not break you could change the check if(player.human) to be if(player && player.human) instead. This will then only evaluate the second half of the if statement when the first half is true (e.g. player is not null).

  7. s3br3m says:

    hey elgar, first of all i’ld like to thank you for the great tutorial series! but i have a problem, the hud textures wont show up in play mode eventhough i assigned the skins, naming conventions seem right aswell… i dont get a compile error but still – no GUI_Skins :/

    • Not sure sorry. Are you able to make changes to other parts of the GUI_Skin and see them have an effect when you run your project? The idea is to try and isolate the problem – in this case we want to work out whether it is a problem with referencing the skin or with usage of the skin.

      • ShockFactor says:

        You may need to drag and drop your OrdersSkin and ResourceSkin (in your Skins folder) to your HUD object (under your Player in the Heirarchy, expanded with arrow button to the left). There should be two empty slots in your HUD script (which should be attached to your HUD Object). Once you fill those slots, your skins should show up in-game.

  8. Hi Elgar
    Great tutorial so far, but I have the same problem as ‘s3br3m’, I have checked and re-checked the script, it all seems fine. HUD is a child of Player game object, I have added the image textures in HUD in the HUD script, also console isnt throwing up any errors, I am a complete loss now. As far as I can tell the the two bars should be showing, but they arent. Any ideas would be greatly appreciated, and also keep up the good work.

    All the best

    Harry

  9. Fredrik Lindell says:

    Hey, does someone know how to deal with this problem;
    NullReferenceException: Object reference not set to an instance of an object
    HUD.OnGUI () (at Assets/Player/HUD/HUD.cs:17)

    I’ve followed this tutorial to the letter, and I can’t seem to find what I do wrong. Here’s the line that Unity states is incorrect;
    void OnGUI () {
    if(player.human) {
    DrawOrdersBar();
    DrawResourceBar();
    }
    }

      • Fredrik Lindell says:

        Though, the thing is, I followed your coding to the letter, and it is still giving me the error. Do you know any way to fix this? I am kinda new to Unity, also.

    • The position of the orders bar is handled by DrawOrdersBar(). To change the position of that would involve change the Rect being used in GUI.BeginGroup() to (0, Screen.height-ORDERS_BAR_HEIGHT, Screen.Width, ORDERS_BAR_HEIGHT) and the Rect being used in GUI.Box() to (0, 0, Screen.Width, ORDERS_BAR_HEIGHT). Then change the ORDERS_BAR_WIDTH to ORDERS_BAR_HEIGHT (rename the variable) and set the desired value.

      However …

      The rest of the tutorial is written with the assumption that the orders bar is on the left. I would strongly recommend that you work through the whole tutorial (or at least a large portion of it) before playing around with repositioning this. This will give you a chance to get a better feel for how things are working in the entire project. Which should make it a bit easier to convert when you do get around to it. There are also some issues mentioned in the comments for Part VI (the comments here on the wordpress site) that would be worth looking over too.

      Also, if you are planning on going through the whole tutorial I would recommend doing so at stormtek.geek.nz/rts_tutorial. Things have been tidied up and there is a much nicer layout overall.

      Hope that helps.

      • Yeah, at about part 5 I’ve decided to fill out all script as you have written, then tweak to my liking. I’ve also increased a few values in ResourceManager to increase camera speed, will this affect anything in the near future?

  10. Yeah, at about part 5 I’ve decided to fill out all script as you have written, then tweak to my liking. I’ve also increased a few values in ResourceManager to increase camera speed, will this affect anything in the near future?

    • Have you added an EmptyObject called HUD (with the script HUD.cs attached to it) to the Player object? If not then the HUD code is unable to locate the Player that it is attached to when it starts up – which would give a null value when you try to call Debug.Log() in your HUD.cs code.

  11. Kenneth says:

    Hi elfarstorm, this is a great tutorial and i am using it in a project i am making at secondaryy school.
    But i have a problem that lots of people have had(sry for my bad english). I have done everything u did, made de scrips, and attached the textures, but i cant make my bars appear on screen. Do you know what the problem might be? or do you know if there is any error on the tutorial?
    Thanks

    • I’m not sure what your problem is sorry. I know there is no problem with the tutorial because a) it works for me and b) lots of other people have it working as well.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s