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

<
>

Part 9: Player Resources

It is now almost time to begin allowing the Player to produce more units and buildings. But before we can do that we should add in resources for the Player, along with a display of what they currently have into the HUD. After all, how can we expect them to spend resources that they do not have?

Resources for the Player

To keep things simple for this tutorial we will introduce two basic resource types: money and power. Money will be used to buy everything that the Player will want to build or upgrade. Power will be used by Buildings (although we will probably not introduce any penalties as part of this tutorial). The main reason for adding power is that we will want to handle power a little differently from resources like money. It provides things for the base, rather than acting as a consumable resource like money. Once we are handling power correctly it should not be hard to add consequences for running out of power.

The first thing we need to do for a Player is to give them the ability to store Resources. As part of this, we are going to make it so that the amount of Resources that a Player can have is limited by storage capacity. So to gain lots of money they are going to need somewhere sensible to store this. For a Player we will determine a base storage capacity (e.g. they can always hold $1000) that can then be expanded during the game. Add the following variables to the top of Player.cs.

				public int startMoney, startMoneyLimit, startPower, startPowerLimit;
				private Dictionary< ResourceType, int > resources, resourceLimits;

To allow the code to make use of Dictionary add

				using System.Collections.Generic;

to the very top of Player.cs. We will use this to map a resource type to a quantity. By doing things this way we can actually write most of our code to handle details independent of the particular resource in question. This allows us to easily add / remove resource types or change what those are. We will use this concept for both the amount of resources a player has and their capacity for that resource. The other 4 variables allow us to play around with how much money and power the player starts with, as well as how much of each of those they can store before needed dedicated storage facilities. If we wish to add more resource types later on we will also need to add more variables for the start values for that resource type.

Let us now add another entry to Enums.cs to define our resource types. We will use the following entry for now.

				public enum ResourceType { Money, Power }

Remember that to access this from Player.cs we also need to add

				using RTS;

to the top of the file.

Now that a Player has somewhere to store resources, let's add in the logic for initializing those values. We need to add the Unity method Awake() to Player.cs with the following code in it.

				void Awake() {
					resources = InitResourceList();
					resourceLimits = InitResourceList();
				}

We will use the method InitResourceList() to provide the initialization for any collection of Resources that needs to be handled - for now just our Resources and the storage capacities for those Resources. Implement this method now using the code below.

				private Dictionary< ResourceType, int > InitResourceList() {
					Dictionary< ResourceType, int > list = new Dictionary< ResourceType, int >();
					list.Add(ResourceType.Money, 0);
					list.Add(ResourceType.Power, 0);
					return list;
				}

The last thing we need to do when setting up our Player is to add their start Resources (and limits). We do this by adding

				AddStartResourceLimits();
				AddStartResources();

to the end of the Start() method in Player.cs and then implementing each of those methods as follows.

				private void AddStartResourceLimits() {
					IncrementResourceLimit(ResourceType.Money, startMoneyLimit);
					IncrementResourceLimit(ResourceType.Power, startPowerLimit);
				}

				private void AddStartResources() {
					AddResource(ResourceType.Money, startMoney);
					AddResource(ResourceType.Power, startPower);
				}

Once again, each of these methods uses helper methods to add values for each resource type, which also need to be implemented.

				public void AddResource(ResourceType type, int amount) {
					resources[type] += amount;
				}

				public void IncrementResourceLimit(ResourceType type, int amount) {
					resourceLimits[type] += amount;
				}

Resource Display

Our Player now has a way to store Resources and the storage limits for each of those, as well as start values for these. Now it is time to display this amount to the Player, which is done through our HUD. We will start by adding

				if(human) {
					hud.SetResourceValues(resources, resourceLimits);
				}

to the Update() method in Player.cs. Now, obviously, we need to provide an implementation of this method within HUD.cs.

				public void SetResourceValues(Dictionary< ResourceType, int > resourceValues, Dictionary< ResourceType, int > resourceLimits) {
					this.resourceValues = resourceValues;
					this.resourceLimits = resourceLimits;
				}

This simply sets the values of the HUD version of resourceValues and resourceLimits to be those specified. This means that we need to declare those at the top of HUD.cs

				private Dictionary< ResourceType, int > resourceValues, resourceLimits;

and initialize them at the beginning of Start().

				resourceValues = new Dictionary< ResourceType, int >();
				resourceLimits = new Dictionary< ResourceType, int >();

With the current values for the Player's Resources being sent to the HUD we are ready to present that information back to the Player. This means updating our DrawResourceBar() method in HUD.cs. Add the following code between GUI.Box() and GUI.EndGroup()

				int topPos = 4, iconLeft = 4, textLeft = 20;
				DrawResourceIcon(ResourceType.Money, iconLeft, textLeft, topPos);
				iconLeft += TEXT_WIDTH;
				textLeft += TEXT_WIDTH;
				DrawResourceIcon(ResourceType.Power, iconLeft, textLeft, topPos);

and then implement the method DrawResourceIcon().

				private void DrawResourceIcon(ResourceType type, int iconLeft, int textLeft, int topPos) {
					Texture2D icon = resourceImages[type];
					string text = resourceValues[type].ToString() + "/" + resourceLimits[type].ToString();
					GUI.DrawTexture(new Rect(iconLeft, topPos, ICON_WIDTH, ICON_HEIGHT), icon);
					GUI.Label (new Rect(textLeft, topPos, TEXT_WIDTH, TEXT_HEIGHT), text);
				}

This simply finds the appropriate image and text for the Resource and then draws them in the specified location. If we ever want to change where a particular Resource value is displayed on screen all we need to change is the values being passed to DrawResourceIcon().

For both of these methods to work properly we need to declare some more constants at the top of HUD.cs.

				private const int ICON_WIDTH = 32, ICON_HEIGHT = 32, TEXT_WIDTH = 128, TEXT_HEIGHT = 32;

We also need to provide a way to drop in and then access textures for our Resource icons. We will use an array of textures so that we can drop in any Resources we like from within Unity

				public Texture2D[] resources;

and then a Dictionary to provide us easy access to those images at runtime.

				private Dictionary< ResourceType, Texture2D > resourceImages;

We just need to add a final bit of initialization into Start() in HUD.cs before we can do any drawing.

				resourceImages = new Dictionary< ResourceType, Texture2D >();
				for(int i = 0; i < resources.Length; i++) {
					switch(resources[i].name) {
						case "Money":
							resourceImages.Add(ResourceType.Money, resources[i]);
							resourceValues.Add(ResourceType.Money, 0);
							resourceLimits.Add(ResourceType.Money, 0);
							break;
						case "Power":
							resourceImages.Add(ResourceType.Power, resources[i]);
							resourceValues.Add(ResourceType.Power, 0);
							resourceLimits.Add(ResourceType.Power, 0);
							break;
						default: break;
					}
				}

Here we are making sure that the appropriate textures are associated with the correct Resources. While we are at it we make sure that we have default values for the amount of Resources and capacities to be displayed. If we want to add more Resources to display (to accept more types) we will need to add a new case into this switch statement for each resource we want to support (as well as adding an entry into the Enum).

Finally we need to provide the textures from inside Unity. First we should create somewhere to store these. Create a folder inside the HUD folder called Icons. Inside here we want to place two 32x32 textures (called Money.png and Power.png) to identify the Resources to the Player. In keeping with the theme of simplicity I have gone with the icons below (but you are more than welcome to construct your own). Note: the names of the textures must match exactly (case sensitive) the values being checked in the switch statement we added just above.

Once you have some images in Unity, drag them onto the Resources field of the HUD attached to your Player. If you run your game now you should see the icons showing up in the resources bar along the top of the screen, along with 0/0 for each Resource (since we have not set starting values for the Player yet), but the text is weirdly positioned. This is because we need to change the settings for Label inside our ResourceSkin. We want to set so that when you run your game things will sit much nicer. There is quite a large gap between the text and the image since we need to allow space for when the Player has lots of Resources (although feel free to play around with the positioning yourself). Let's start the Player off with $1000, the ability to hold $2000, 500 units of power, and a power capacity of 500 units. If you run the game now you should see that the Player has 1000/2000 for money and 500/500 for power.

I think that brings us neatly to then end of this part. For the final version of the code from this part check out the commit for Part 9 on github. Check out the next part for the ability to create new Units from Buildings, complete with a nice build queue to handle creating multiple Units at once.

<
>