Mobile Demo

I have been working on a demo mobile game using Unity3D (targeting Android at the moment – a Samsung Galaxy Note2 to be precise). This is mainly to see what sorts of things can be done. I have an idea for a game that I will be using this trial as a foundation for. I will not be writing anything serious up about it (since that would take up too much of my time). But I thought that some people may be interested in checking out the code (and following the changes I make to it). It is all up on my github account under the project unity-mobile-demo.

For those of you following the RTS tutorial: Don’t worry, this is not a replacement for that. I am still working on the tutorial. This is a much smaller project that I can work on in small snippets (rather than the extended period of time required to write a tutorial post). I started playing with it because I had too many ideas buzzing round in my head.

Creating an RTS in Unity: Part XVII

At long last I have managed to get another post written. I think this has turned out to be one of the longest in the tutorial so far … The topic this time enabling the user to select which Player they want to be. This includes saving and loading basic Player details so that you can select the Player you were last time you played. Some basic customization is provided too – name and an avatar. This part can be found at stormtek.geek.nz/rts_tutorial/part17.php. Enjoy!

P.S. While writing this part I fixed a small bug with the mouse cursor. We are using MouseCursorSkin to provide basic styling for the cursor (although the actual image being used depends on the current cursor state). I have set the border, padding, and margin for a Label to be 0. This ensures that the image we are using is displayed as large as possible.

Plans for the coming year

Sorry for the long delay between posts. My intention is still to work on this tutorial. I can see that it is useful for other people, but it is also useful for me to think through why I am doing things. It works out as a learning experience for all of us!

Anyway, I thought it might be a good idea to sketch out for you my plans for the next stage of this tutorial. I am hoping to put up a new part once a month. I know, this is slow for those of you who are working through the tutorial right now and just want to carry on with things. But this is the most realistic time frame for me to aim for at the moment. If you are lucky there might be more than one part per month, but that is in no way guaranteed.

The reason this is plans for the coming year is because I have sketched out where I want to take the next 10 parts – which will take until the end of the year to complete if I can do 1 per month. I know, another 10 parts still, and at that point there will still be lots of things that could be worked on. But by that point we will have a stable platform to build on with a lot of very useful things in place. So, the things I plan to work on next are:

  • Character selection
  • Saving a game
  • Loading a game
  • Victory conditions (there will be more than one that we define, so that you have options to choose from)
  • Single player campaign (basic but should be very easy to extend)
  • Basic computer player (so we can finally make things interactive)
  • Power (remember it was another resource type we introduced?)
  • Waypoints (so you can set up paths for your Units to follow)
  • Audio (since a quiet game is kinda boring)
  • Options menu (so that Players can turn that audio down, among other things)

So, lots of interesting things still coming up. Thank you all for your patience as I work on things. Thank you also for your support and encouragement – it does inspire me to keep working on things. And don’t worry, I have not forgotten about this.

Also, a reminder that the entire tutorial is now up on stormtek.geek.nz.

Dedicated Website

stormtek.geek.nz

Hey team. I have finally managed to get a dedicated website up and looking good. Check it out using the link posted at the top of this post. The main improvement this brings over the blog is that the tutorial is now nice and easy to read in its entirety. Nice clean navigation. Ease of access. So much better in so many ways. (And I have full control over everything which is nice too) Please check it out and let me know what you think. (There are still some adjustments I will be making, but overall it is stable and looking good)

I will be going through each of the original posts to provide a link to the new site. When I add new parts to the tutorial I will write a blog post about that, complete with a link to that part, so that if you follow the blog you can stay up-to-date. However, the full content of new parts will be on the website rather than here on the blog (since that saves duplication of effort). I will also look at providing links back to the original posts so that comments which have been added so far can be referenced from the new site.

Status Update

Hey team,

I have not forgotten about you all. I am still wanting to finish this tutorial, and to continue to add interesting and useful posts to it. However, other things in life have to take priority from time to time. Now that Christmas and holidays are over I should be able to slowly start getting some more posts out. I am also working on actually setting up a website of my own (rather than just using the blog here). A large part of this will be having the entire tutorial in one place. This will feature actual navigation between parts and should be a whole lot more user friendly. Once that is up I will be adding content directly there, but I will post here with the link to the latest part so that people can still easily access things from here.

I appreciate all of your interest and support so far. It has surprised me just how many people are actually interested in this. I thought there was a bit of a gap, but I didn’t realize how much of a gap that was. I aim to wrap things up in the next year – sorry but I can’t promise anything sooner than that. The next post should be up in the next month, but again I can’t promise.

Continue messing around, having fun, and learning lots.

Update: the website is now live at stormtek.geek.nz

Creating an RTS in Unity: Part XVI

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

Now that we have a basic menu in place it is time to extend that a bit. As you are aware, a game does not typically just start when you open it. The standard starting point is a main menu, from which you actually launch the game. The goal this time is to create a main menu, start a game from this menu, and add the ability to return to the menu from our game.

Generic Menu

The basic layout for our MainMenu is going to be similar to that of the PauseMenu that we created last week. Our interactions with it are going to be different, but the overall structure should at least be familiar. In fact, the structure is so similar that we should actually make a base Menu class which does most of the work for us. Inside the Scripts folder located in the Menu folder create a new C# script and call it Menu.cs. Set the code to be the following.

using UnityEngine;
using System.Collections;
using RTS;

public class Menu : MonoBehaviour {

	public GUISkin mySkin;
	public Texture2D header;

	protected string[] buttons;

	protected virtual void Start () {
		SetButtons();
	}

	protected virtual void OnGUI() {
		DrawMenu();
	}

	protected virtual void DrawMenu() {
		//default implementation for a menu consisting of a vertical list of buttons
		GUI.skin = mySkin;
		float menuHeight = GetMenuHeight();

		float groupLeft = Screen.width / 2 - ResourceManager.MenuWidth / 2;
		float groupTop = Screen.height / 2 - menuHeight / 2;
		GUI.BeginGroup(new Rect(groupLeft, groupTop, ResourceManager.MenuWidth, menuHeight));

		//background box
		GUI.Box(new Rect(0, 0, ResourceManager.MenuWidth, menuHeight), "");
		//header image
		GUI.DrawTexture(new Rect(ResourceManager.Padding, ResourceManager.Padding, ResourceManager.HeaderWidth, ResourceManager.HeaderHeight), header);

		//menu buttons
		if(buttons != null) {
			float leftPos = ResourceManager.MenuWidth / 2 - ResourceManager.ButtonWidth / 2;
			float topPos = 2 * ResourceManager.Padding + header.height;
			for(int i=0; i<buttons.Length; i++) { 				if(i > 0) topPos += ResourceManager.ButtonHeight + ResourceManager.Padding;
				if(GUI.Button(new Rect(leftPos, topPos, ResourceManager.ButtonWidth, ResourceManager.ButtonHeight), buttons[i])) {
					HandleButton(buttons[i]);
				}
			}
		}

		GUI.EndGroup();
	}

	protected virtual void SetButtons() {
		//a child class needs to set this for buttons to appear
	}

	protected virtual void HandleButton(string text) {
		//a child class needs to set this to handle button clicks
	}

	protected virtual float GetMenuHeight() {
		float buttonHeight = 0;
		if(buttons != null) buttonHeight = buttons.Length * ResourceManager.ButtonHeight;
		float paddingHeight = 2 * ResourceManager.Padding;
		if(buttons != null) paddingHeight += buttons.Length * ResourceManager.Padding;
		return ResourceManager.HeaderHeight + buttonHeight + paddingHeight;
	}

	protected void ExitGame() {
		Application.Quit();
	}
}

The core of this code is what we had in PauseMenu.cs last time. A couple of things have been adjusted to allow us to specify different variations easily. By default we will not specify a value for our array of buttons. A specific menu can define this array through the method SetButtons(). The check

buttons != null

is therefore needed in a number of places to make sure that we only use the buttons array if it is specified. This means that by default a menu will actually just have the header image with no buttons underneath it. By using GetMenuHeight() we can define the height of the menu based on the number of buttons that are specified, all nicely padded and looking tidy. The final method that allows variation is HandleButton(). The basic menu has no knowledge of which buttons will be present and therefore it has no idea what to do with a button when it is clicked. A specific menu can use this method to define how to handle the buttons that it has defined. We will also add the method that handles exiting the game to Menu.cs since we want to be able to call this from any menu that wants it.

Before we carry on we should tidy up our PauseMenu from last time, making sure that it extends our newly created Menu. We should also make sure that it still behaves as expected. Update PauseMenu.cs so that it now has the following code in it.

using UnityEngine;
using RTS;

public class PauseMenu : Menu {

	private Player player;

	protected override void Start () {
		base.Start();
		player = transform.root.GetComponent();
	}

	void Update () {
		if(Input.GetKeyDown(KeyCode.Escape)) Resume();
	}

	protected override void SetButtons () {
		buttons = new string[] {"Resume", "Exit Game"};
	}

	protected override void HandleButton (string text) {
		switch(text) {
			case "Resume": Resume(); break;
			case "Exit Game": ExitGame(); break;
			default: break;
		}
	}

	private void Resume() {
		Time.timeScale = 1.0f;
		GetComponent().enabled = false;
		if(player) player.GetComponent().enabled = true;
		Screen.showCursor = false;
		ResourceManager.MenuOpen = false;
	}

}

As you can see, this is a much simpler class now. The methods SetButtons() and HandleButton() define what buttons are present and how to handle the click for each of them. Note that in Start() we need to make sure that we call base.Start() – this makes sure that any initialization specified in the base class is still performed. If you now run your project now you should see that the pause menu still behaves the same as before.

Main menu

Now that we have a base implementation of a menu in place it is time to create our main menu. The first thing we actually want to do is to create a new Scene for our game. We will use this to manage our main menu (which will get more complex over time). Create a new scene (File -> New Scene) and then save it inside the Menu folder. Next, create a new C# script inside the Scripts folder located in the Menu folder and call this MainMenu.cs. Add this script to the camera object so that we can use it shortly. Note: we could create an empty object to attach scripts to, but I do not think that is needed for this scene.

Now it is time to specify the code for our main menu. Set MainMenu.cs to contain the following code.

using UnityEngine;
using System.Collections;
using RTS;

public class MainMenu : Menu {

	protected override void SetButtons () {
		buttons = new string[] {"New Game", "Quit Game"};
	}

	protected override void HandleButton (string text) {
		switch(text) {
			case "New Game": NewGame(); break;
			case "Quit Game": ExitGame(); break;
			default: break;
		}
	}

	private void NewGame() {
		ResourceManager.MenuOpen = false;
		Application.LoadLevel("Map");
		//makes sure that the loaded level runs at normal speed
		Time.timeScale = 1.0f;
	}
}

Once again we specify the buttons we want to have and how to handle the click on each of them. The only interesting code in this class is found in the NewGame() method. Here we are wanting to start a new game using the map that we have been working on for the majority of this tutorial. Unity provides us the ability to change scenes with the use of Application.LoadLevel(). However, for this to work we need to know a couple of things. First up, we need to know the name of the Scene that we want to change to. This is the parameter that gets passed in to LoadLevel(). It is all well and good knowing the name of the scene that we want to load, but the more important thing is to let Unity know all of the scenes that we have in our game. This is done through the build settings for our project (File -> Build Settings). At the top of the dialog that shows up is a list of scenes that are to be included in our build. Any of the scenes in this list can be loaded by using Application.LoadLevel(). To add a scene to this list find the appropriate scene in your Assets directory (wherever it was saved to) and drag it onto the list. The first scene in the list is the one that is loaded when Unity starts up, so we want to make sure that our MainMenu scene is listed first. Then we need to add our Map scene so that we can load it from the MainMenu. Your Build Settings should look like the screenshot below.

Build Settings

Build Settings

To make sure that the main menu is styled correctly and shows the header image we need to attach MenuSkin to the MySkin field of the MainMenu that we attached to the camera earlier. We also need to add our header to the header field.

With this done you should be able to run your game (starting in the MainMenu scene) and see a menu show up with the options “Start Game” and “Quit Game”. Clicking “Start Game” should load up the familiar map from the rest of the tutorial.

Updated Pause Menu

With this in place we actually want to change the behaviour of our pause menu slightly. At the moment when we click “Exit Game” from the pause menu we are exiting our game. What we actually want to do is to return to the main menu, rather than closing the game. To do this we need to change the case for “Exit Game” in HandleButton() to call

ReturnToMainMenu()

rather than

ExitGame()

like it does at the moment. We then need to set the definition of ReturnToMainMenu() as follows.

private void ReturnToMainMenu() {
	Application.LoadLevel("MainMenu");
	Screen.showCursor = true;
}

This loads the MainMenu scene again and makes sure that the normal cursor is shown for the menu. Ideally, this method should prompt the player for confirmation that they wish to exit their game, along with the option to save it or not. We will add this in once we have added the ability to save games in a couple of posts. Feel free to play round with a confirm prompt if you like though.

And as simple as that we now have our game starting from a menu, the ability to start a new game using the map we have been working on, and the ability to return to the main menu from within our game. The entire project can be found on github with the code for this part under the commit for Part 16.

Fixed missing parameter in old posts

As some of you may recall, I recently noticed an error where parameters being sent to GetComponent() (and similar calls) were not showing up. I believe this is being caused by formatting being performed by WordPress that results in the parameters being stripped out because they are in angle brackets. I apologize for any difficulties this has caused people as they have been attempting to follow along. I have now gone back and fixed all (or at the very least the majority) of these occurrences in the first 15 parts of the tutorial. Hopefully this means that no more people have this problem in the future.

Note: For anyone wanting to post comments including these parameters, simply add some whitespace either side of the parameter, as that seems to fix the problem.

Creating an RTS in Unity: Part XV

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

From what we have done so far we now have the broad framework for a real time strategy game in place. We can control units and buildings, create new units and buildings, and destroy enemy units and buildings. We can also collect resources to fund the creation of those new units and buildings. Sure, there are plenty of gaps that need filling. Pathfinding, collisions, an AI to control computer players, an AI to control individual units (and defensive buildings), the list goes on. At a later date I may come back and fill in some of those details, but for now we will leave that part of the game there.

However, there are more things that we need to put into place before we have a complete package. At the moment we are stuck with a single static map. Any time a player wants to play they must start at the same place. This might work well for arcade games, but it is not what we expect when we come to play a real time strategy game. Typically these have a much larger scope than the simple arcade game does.

The last few parts of this tutorial are therefore going to be about adding the things we need to flesh out the UI and overall user experience. This will involve adding in a menu system, the ability to save games and then to reload them, user accounts so that we can personalize the experience a little bit, and then at the end we will implement a basic single-player campaign.

Simple menu system

To get things started we will implement a basic pause menu that can be triggered from within our game. Initially this will not do much, but it will lay the platform for more complicated menu interactions later on. We want this to be triggered from user input, so we need to adjust the Update() method in UserInput.cs. Add

if(Input.GetKeyDown(KeyCode.Escape)) OpenPauseMenu();

immediately before the call to MoveCamera(). Here we are specifying that when the escape key is hit we want to open the pause menu. That method needs to be defined before we can continue.

private void OpenPauseMenu() {
	Time.timeScale = 0.0f;
	GetComponentInChildren< PauseMenu >().enabled = true;
	GetComponent< UserInput >().enabled = false;
	Screen.showCursor = true;
	ResourceManager.MenuOpen = true;
}

The first line tells Unity to basically stop time. If we do not do this then actions initiated before the menu was opened carry on – so Units keep moving, etc. In the second line we are enabling the script that will handle the pause menu itself. Then we disable the user input script, since we want the Player to be interacting with the menu, rather than the game. The fourth line re-enables the system cursor, so that we do not have to implement a custom cursor for use within menus (although you could do so if you wished). Finally we are letting our ResourceManager know that a menu is open at the moment. This gives us an easy way for code to check whether a menu is open or not. To be able to set this value we need to add it to ResourceManager.cs.

public static bool MenuOpen { get; set; }

Note: this is the CSharp way of setting up a public getter and setter on a private variable.

The next important step is to create the script that will handle the pause menu. We will create a new folder to hold all menu related things (since we will be building up quite a bit over the next few posts). Inside the main assets folder create a new folder called Menu. Inside this folder create a new folder call Scripts and then inside this folder create a new CSharp script called PauseMenu.cs. Set the initial code to the code that follows.

using UnityEngine;
using RTS;

public class PauseMenu : MonoBehaviour {

	public GUISkin mySkin;
	public Texture2D header;

	private Player player;
	private string[] buttons = {"Resume", "Exit Game"};

	void Start () {
		player = transform.root.GetComponent< Player >();
	}

	void Update () {
		if(Input.GetKeyDown(KeyCode.Escape)) Resume();
	}
}

We are defining a couple of public variables that we can set within Unity – a GUISkin that we can use to adjust the look and feel of the menu quickly and easily, and a header image that we want to display at the top of the menu. We then have a reference to the player this menu belongs to (initialised in the usual way in the Start() method), and a list of the buttons we want to display (represented by the strings we want to display on each button). In the Update() method we are also defining that we want to use the escape key as a shortcut to resume gameplay. Let us now define the Resume() method.

private void Resume() {
	Time.timeScale = 1.0f;
	GetComponent< PauseMenu >().enabled = false;
	if(player) player.GetComponent< UserInput >().enabled = true;
	Screen.showCursor = false;
	ResourceManager.MenuOpen = false;
}

This method is basically the inverse of the method that we used to open the pause menu in the first play. We restore the time scale back to 1, disable the pause menu, find and re-enable the UserInput for the correct player, hide the system cursor, and tell the ResourceManager that the menu has now been closed.

The actual drawing of the menu will be handled by the OnGUI() method provided by Unity, which we need to implement now.

void OnGUI() {
	GUI.skin = mySkin;

	float groupLeft = Screen.width / 2 - ResourceManager.MenuWidth / 2;
	float groupTop = Screen.height / 2 - ResourceManager.PauseMenuHeight / 2;
	GUI.BeginGroup(new Rect(groupLeft, groupTop, ResourceManager.MenuWidth, ResourceManager.PauseMenuHeight));

	//background box
	GUI.Box(new Rect(0, 0, ResourceManager.MenuWidth, ResourceManager.PauseMenuHeight), "");
	//header image
	GUI.DrawTexture(new Rect(ResourceManager.Padding, ResourceManager.Padding, ResourceManager.HeaderWidth, ResourceManager.HeaderHeight), header);

	//menu buttons
	float leftPos = ResourceManager.MenuWidth / 2 - ResourceManager.ButtonWidth / 2;
	float topPos = 2 * ResourceManager.Padding + header.height;
	for(int i=0; i<buttons.Length; i++) {
  		if(i > 0) topPos += ResourceManager.ButtonHeight + ResourceManager.Padding;
		if(GUI.Button(new Rect(leftPos, topPos, ResourceManager.ButtonWidth, ResourceManager.ButtonHeight), buttons[i])) {
			switch(buttons[i]) {
				case "Resume": Resume(); break;
				case "Exit Game": ExitGame(); break;
				default: break;
			}
		}
	}

	GUI.EndGroup();
}

There are several important sections here where work is being done. First off we want to set the GUISkin being used to be the one attached to the pause menu. Next we want to define the part of the screen that we will be draw in, which will be a rectangle in the centre of the screen defined by GUI.BeginGroup(). Then we draw a background rectangle for the menu followed by adding the header at the top of this box. Finally we want to draw a vertical list of buttons below the header. Note: Unity includes the click handler for a button in with the definition of where to draw the button. We can determine which button was clicked by evaluating the text of that button. Once we know this we can call the appropriate method to handle the click for that button.

There is an extra method that this code requires us to define now

private void ExitGame() {
	Application.Quit();
}

which simply tells the Unity application to quit.

You will notice that there are lots of references to values in ResourceManager being used in the OnGUI() method. This is so that we can separate the details of layout from the logic of the creation of the menu. In order for things to work we need to define all of these inside ResourceManager.cs.

private static float buttonHeight = 40;
private static float headerHeight = 32, headerWidth = 256;
private static float textHeight = 25, padding = 10;
public static float PauseMenuHeight { get { return headerHeight + 2 * buttonHeight + 4 * padding; } }
public static float MenuWidth { get { return headerWidth + 2 * padding; } }
public static float ButtonHeight { get { return buttonHeight; } }
public static float ButtonWidth { get { return (MenuWidth - 3 * padding) / 2; } }
public static float HeaderHeight { get { return headerHeight; } }
public static float HeaderWidth { get { return headerWidth; } }
public static float TextHeight { get { return textHeight; } }
public static float Padding { get { return padding; } }

Here we have defined some private variables that form the basis of our layout. We use these, in conjunction with some CSharp getters, to define the actual values that are to be returned.

There are just a couple of things to do before we can run our project and display the menu. Inside the Menu folder create two new folders called Images and Skins. Inside the skins folder create a new GUISkin called MenuSkin. Inside the Images folder we want to add a header image to display at the top of the menu. This could be a logo, something that provides a bit more theming for your game. I have gone for a simple placeholder like the image below.

Menu header image

Menu Header Image

We now have all of the components for opening our pause menu, but they need to be attached somewhere logical before we can use them. What we want to do is to add them to the HUD object belonging to each of our Players. This makes sense, since they are part of the display being presented to the Player. However, we do not want to have to attach one to each of the players in the game. The way to handle this is to add them to the player prefab that we created. In order to do this first add a new instance of that prefab into your game. We will use this instance to edit and update the prefab (since the other players already in the game already have a lot of other things added that we do not want to be part of a new player). Now attach the PauseMenu script to the HUD of the new Player. Add the header image and the MenuSkin to the appropriate fields of the PauseMenu you just attached. Once you have done this, make sure to disable the PauseMenu script, since the default state for our game is ‘playing’ rather than ‘paused’. You can do this by unchecking the checkbox next to the title of the script. To save these changes back to the prefab drag this new Player object onto the Player prefab in the Player folder. You should be prompted whether you want to do this. When this prompt shows up you need to choose the ‘save over’ option, since we want to replace the existing prefab with the updated prefab. These changes should now be present in all of the players already in our game.

The main customization I have done on MenuSkin is to change the background image that is used on the box element. I set the background image for a normal box to the image below.

Menu background

Menu Background

You should now be able to run your game and open the newly created pause menu by using the escape key. Clicking on resume, or hitting escape, should return you to your game. Once you have built your game clicking exit should close your game (this does not work for some reason when running your game from within Unity).

And that wraps things up for this week. Our game now contains a simple pause menu. As I mentioned earlier, we will build on this over the next couple of posts. All of the code for this post can be found on github under the commit for Part 15.

Missing parameter for call to GetComponent

Hey,

I have just noticed that wordpress is removing some of the code from my source code … I know that some helpful people have pointed this out a couple of times already, but this is the first time I have had a serious look. My apologies for all of the trouble this has caused people (mainly due to compile errors you would have got). Every time GetComponent() is called it is expecting a type being passed in angle brackets. For example

WorldObject worldObject = hitObject.transform.root.GetComponent< WorldObject >();

rather than

WorldObject worldObject = hitObject.transform.root.GetComponent();

which is how all of the code published in the blog so far has come out as. The type to be passed in is the type that you are saving the object as. So in the example above we are storing a WorldObject, so we need to put WorldObject between the angle brackets. I will go through all of the posts in the near future and tidy this up so that in the future people do not have this problem.

Edit: It seems that this may not even be consistent … Note to anyone trying to comment with corrections, it seems to need spaces between the angle brackets and the type in order to show more consistently.

Creating an RTS in Unity: Part XIV

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

At long last it is time for a new post! I apologize for the delay. It has taken a lot longer to get this post together than I had initially planned. I knew it was going to be a while, but I did not expect that to stretch into several months. For that I am truly sorry.

This time round we will cover combat, the fun part of a game where you get to destroy things! We will only initiate this from UserInput for now, although our WorldObjects will be set up in such a way that they could easily be told to start attacks automatically through triggers of some sort. An important part of this will also be determining what happens to a WorldObject as it loses hitpoints.

Attack Cursor

Let’s start by detecting whether the Player can start an attack at the moment. We will indicate this to the user by changing the mouse cursor to an attack cursor. We need to start by extending SetHoverState() in WorldObject.cs. Replace the existing code with the code below

public virtual void SetHoverState(GameObject hoverObject) {
	//only handle input if owned by a human player and currently selected
	if(player && player.human && currentlySelected) {
		//something other than the ground is being hovered over
		if(hoverObject.name != "Ground") {
			Player owner = hoverObject.transform.root.GetComponent< Player >();
			Unit unit = hoverObject.transform.parent.GetComponent< Unit >();
			Building building = hoverObject.transform.parent.GetComponent< Building >();
			if(owner) { //the object is owned by a player
				if(owner.username == player.username) player.hud.SetCursorState(CursorState.Select);
				else if(CanAttack()) player.hud.SetCursorState(CursorState.Attack);
				else player.hud.SetCursorState(CursorState.Select);
			} else if(unit || building && CanAttack()) player.hud.SetCursorState(CursorState.Attack);
			else player.hud.SetCursorState(CursorState.Select);
		}
	}
}

and add the method CanAttack() which we will use to determine whether a specific WorldObject is able to attack things or not.

public virtual bool CanAttack() {
	//default behaviour needs to be overidden by children
	return false;
}

Notice that we have declared the method virtual to allow a class which extends WorldObject to override the default behaviour if necessary. By default a WorldObject will not be able to attack.

The logic being used for setting the hover state is as follows.

  • By default we want to show the selection cursor
  • If the WorldObject being hovered over is a Unit or a Building that is a) not owned by a Player or b) owned by another Player and the WorldObject that is being controlled can attack then show the attack cursor

If we wish to implement teams at some point then this code will need updating to make sure that the other Player is not on the same team as the Player that controls the selected WorldObject, rather than just checking that they are a different Player. There is also an assumption here that Players are not allowed to have the same name, since that is what we are using to differentiate between them.

For now we have just the one WorldObject that can attack things – our Tank. To allow it to be able to attack things add the following code to Tank.cs.

public override bool CanAttack() {
	return true;
}

We actually defined drawing of the attack cursor a number of posts back when we first added in custom cursors, so this code is enough to handle the changing of the cursor. If your objects still match mine then you should have a Building that is not attached to the Player. If you run your game now, select your Tank, and then hover your cursor over this Building you should be able to see the cursor change correctly.

Extra Player

To make things a little easier to test, though, we should add in another Player and give them some Units and Buildings. I think we have also reached the stage now where we can actually create a Player prefab too.

Before we create the prefab we should strip out everything that we do not want a brand new Player to have. This means we want to delete all Units and Buildings currently attached to our Player. (Before you do this make sure that each of them has already been made into a prefab, complete with BuildImages) You should be left with a Player object that contains a Buildings object and a Units object (both empty), an HUD object, and a RallyPoint object. Both the RallyPoint and the HUD should also be prefabs, so that we can make changes to the prefab and know that those changes will be applied to all Players that have an instance of those. Once your Player looks like this, turn it into a prefab (stored in the Player folder).

Now that we have a Player prefab we can add another instance of Player to the map. Set the name for one Player to Player1 and the name for the other Player to Player2. This makes sure that the two Players will be different if we compare them. Make Player1 a Human player (so that we can control that Player) and Player2 a Computer player (so that we have no control). With two Players in place add some Units and Buildings to each of them. We can do this by dragging new Building prefabs onto the Buildings part of our Player object and new Unit prefabs onto the Units part our our Player object. I have given each player a Refinery and a WarFactory, along with a Harvester, a Worker, and 2 Tanks. (Make sure that you spread these around the map a bit too. This is also a good time to make sure that the Position and Rotation for each prefab is (0, 0, 0))

If you run your game now you will see a collection of Units and Buildings scattered around. Unfortunately, we have no easy way to identify who owns what. We are only able to control the Units and Buildings of Player1, but the only way to tell what those are at the moment is to click on something and see if it responds. This is less than ideal.

Team Colour

To help an actual player to determine what is theirs we are going to introduce the ability to colour part of a WorldObject with a team colour. We will leave it up to a specific object to determine what this will be. Let us start by adding

if(player) SetTeamColor();

to the end of the Start() method in WorldObject.cs and then defining the method SetTeamColor().

protected void SetTeamColor() {
	TeamColor[] teamColors = GetComponentsInChildren< TeamColor >();
	foreach(TeamColor teamColor in teamColors) teamColor.renderer.material.color = player.teamColor;
}

This simply finds all the child objects of the WorldObject with the script TeamColor.cs attached and changes their colour to the team colour for the Player. Of course, we need to create this script before we can use this code. Create a new CSharp script in the WorldObject folder, name it TeamColor.cs, and set it’s contents as follows.

using UnityEngine;

public class TeamColor : MonoBehaviour {
	//wrapper class to allow a user-defined
	//part of a model to receive a team color
	//in the material of that part
}

Next we need to assign a team colour to a Player. Add

public Color teamColor;

to the top of Player.cs. This allows us to now set a team colour for each Player inside Unity. I have made Player1 to be a light blue (R: 100, G: 200, B: 250) and Player2 to be a medium red (R: 235, G: 20, B: 20).

Finally we need to define the parts of each WorldObect that we want to be the team colour. To do this is as easy as attaching the script TeamColor.cs to the desired object. The thing is, we want to perform this attachment on the prefab, not on an object already assigned to a Player. This is because we want to be able to guarantee that we do not have to perform this action every time we add a new object to a Player. This means that we need to delete all of the objects we just added to the Players, update the prefabs (which will require adding a copy to the map, deleting the prefab, making the changes, and then recreating the prefab), and then add the objects to the Players again. I will leave it up to you to decide which parts should be the team colour, though feel free to have a look at my version (found on github). Now when you run your game it should be easy to see which Player controls which Units and Buildings.

Note: If we delete a prefab that is referenced in GameObjectList we will need to remove the entry for it there. It is probably worthwhile at this stage checking to see that GameObjectList is up-to-date, since we are using it as the storage container for all objects we wish to create in our world.

If you have been paying close attention to how things ran prior to this post you will notice that we have introduced a subtle bug. Our Units and Buildings now all have a part of them designated to be a team colour, which is great for identifying which Player it belongs to. Unfortunately, when we create a new Building it is transparent during the build process. Once construction is completed it reverts to the colours it had, but it forgets the team colour. We need to fix this, since construction is going to be the primary way that our Players gain new Buildings. It actually turns out that this is really easy to fix. All we need to do is add a call to SetTeamColor() to the end of Construct() in Building.cs, resulting in the following code.

public void Construct(int amount) {
	hitPoints += amount;
	if(hitPoints >= maxHitPoints) {
		hitPoints = maxHitPoints;
		needsBuilding = false;
		RestoreMaterials();
		SetTeamColor();
	}
}

Initiate Attack

Now that we can distinguish between Players and we can indicate to a Player that an attack can be launched it is time to actually start that attack. We start by updating the code in MouseClick() found in WorldObject.cs.

public virtual void MouseClick(GameObject hitObject, Vector3 hitPoint, Player controller) {
	//only handle input if currently selected
	if(currentlySelected && hitObject && hitObject.name != "Ground") {
		WorldObject worldObject = hitObject.transform.parent.GetComponent< WorldObject >();
		//clicked on another selectable object
		if(worldObject) {
			Resource resource = hitObject.transform.parent.GetComponent< Resource >();
			if(resource && resource.isEmpty()) return;
			Player owner = hitObject.transform.root.GetComponent< Player >();
			if(owner) { //the object is controlled by a player
				if(player && player.human) { //this object is controlled by a human player
					//start attack if object is not owned by the same player and this object can attack, else select
					if(player.username != owner.username && CanAttack()) BeginAttack(worldObject);
					else ChangeSelection(worldObject, controller);
				} else ChangeSelection(worldObject, controller);
			} else ChangeSelection(worldObject, controller);
		}
	}
}

There are a couple of things to note here. First, we only allow a Player to attack objects that belong to another Player. Secondly, a Player cannot attack objects that belong to them. Finally, an attack is only launched if the object can launch attacks. Also, the default behaviour is to change the selected object. With this code in place we now need to define the method BeginAttack(), which is where all the work of starting an attack will actually happen.

protected virtual void BeginAttack(WorldObject target) {
	this.target = target;
	if(TargetInRange()) {
		attacking = true;
		PerformAttack();
	} else AdjustPosition();
}

The logic here is to only attack if the target is close enough. If not the WorldObject needs to move closer to the target. But before we do that we want to save the target that is to be attacked. This means that we need to add

protected WorldObject target = null;

to the top of WorldObject.cs so that we can store this reference. Next we need to add

protected bool attacking = false;

to the top of WorldObject.cs, which we will use to indicate whether this object is attacking something or not. Finally, we need to add the three methods which handle the working out of the logic for this method. We will start with working out whether the target is in range or not.

private bool TargetInRange() {
	Vector3 targetLocation = target.transform.position;
	Vector3 direction = targetLocation - transform.position;
	if(direction.sqrMagnitude < weaponRange * weaponRange) {
		return true;
	}
	return false;
}

The target is in range if the distance to the target is less than the range of the weapon for the WorldObject. For us to know what this range is we need to add

public float weaponRange = 10.0f;

to WorldObject. By specifying a value here we can guarantee that each WorldObject has a default range (I have gone through and added defaults to most values now so that a new type of WorldObject will work without weird errors). Since it is public it also means that we can play around with the range inside Unity.

While looking into how best to work out the distance to the target I found docs.unity3d.com/Documentation/Manual/DirectionDistanceFromOneObjectToAnother.html which notes that for a simple distance calculation we can use the square of the magnitude, since that takes less work to calculate. This is what makes a seemingly simple check look a little more complicated, but every little bit of time saved in processing is worth it in the long run.

Now that we know whether a target is in range or not, let’s specify how to adjust the position of our WorldObject.

private void AdjustPosition() {
	Unit self = this as Unit;
	if(self) {
		movingIntoPosition = true;
		Vector3 attackPosition = FindNearestAttackPosition();
		self.StartMove(attackPosition);
		attacking = true;
	} else attacking = false;
}

At the moment the only object that can move closer to a target is a Unit. If the WorldObject is not a Unit then we cancel the attack (since it is not possible anyway). Otherwise we want to find the closest point between the WorldObject and the target which is in range and then start moving towards that. This method that performs this calculation needs to be added in now.

private Vector3 FindNearestAttackPosition() {
	Vector3 targetLocation = target.transform.position;
	Vector3 direction = targetLocation - transform.position;
	float targetDistance = direction.magnitude;
	float distanceToTravel = targetDistance - (0.9f * weaponRange);
	return Vector3.Lerp(transform.position, targetLocation, distanceToTravel / targetDistance);
}

We know where the WorldObject is, and we know where the target is. From this we can calculate the vector between the two objects. This vector gives us the distance to the target, from which we want to subtract the weapon range. We will actually subtract 90% of the weapon range so that once the Unit is in position it will not need to move immediately if the target should move. This gives us the distance that we need to travel along the vector between the WorldObject and the target. We then use all of these details to execute the Unity linear interpolation method on a Vector3 to calculate the position in the world that is the correct distance along the vector. This is the position that needs to be returned as the nearest attack position.

We also need to add

protected movingIntoPosition = false;

to the top of WorldObject.cs to help with us working out what the current state of our WorldObject is. To make sure that a Unit updates it’s state upon completion of movement we also need to add

movingIntoPosition = false;

into MakeMove() in Unit.cs when the Unit has reached it’s destination. The resulting method is as follows.

private void MakeMove() {
	transform.position = Vector3.MoveTowards(transform.position, destination, Time.deltaTime * moveSpeed);
	if(transform.position == destination) {
		moving = false;
		movingIntoPosition = false;
	}
	CalculateBounds();
}

Now that we know how to get into range it is time to define how to perform an attack.

private void PerformAttack() {
	if(!target) {
		attacking = false;
		return;
	}
	if(!TargetInRange()) AdjustPosition();
	else if(!TargetInFrontOfWeapon()) AimAtTarget();
	else if(ReadyToFire()) UseWeapon();
}

This method is going to be called regularly while the WorldObject is attacking, so we will add in some extra checks that are useful at the same time. The first one is to make sure that the target has not been destroyed already (by this WorldObject or some other WorldObject). If it has we need to stop the attack at once. Again we check to see whether the target is in position, since it might have managed to move away from the WorldObject. Next we check to see whether the target is in front of the weapon the WorldObject has and adjust position accordingly. Finally we check to see whether the weapon is ready to fire (since we want to enforce a regular rate of fire) and only actually use the weapon if the WorldObject is ready to.

Again we need to add some more code to our WorldObject. Let’s start with aiming the weapon.

private bool TargetInFrontOfWeapon() {
	Vector3 targetLocation = target.transform.position;
	Vector3 direction = targetLocation - transform.position;
	if(direction.normalized == transform.forward.normalized) return true;
	else return false;
}

The check is quite straightforward. If the vector between the WorldObject and the target equals the forward vector for the WorldObject then the target is in front of the WorldObject. At this stage we are assuming that the weapon for a WorldObject always points out the front of the WorldObject. If you wish to change this for a specific object then that object will need to override this method (in which case this will need to be a protected virtual method, rather than a private method).

protected virtual void AimAtTarget() {
	aiming = true;
	//this behaviour needs to be specified by a specific object
}

We will leave most of the behaviour for aiming at a target up to a specific object. All we want to do is to set a state variable so that we can work out whether the WorldObject is aiming their weapon or not. This needs to be added to the top of WorldObject now.

protected bool aiming = false;

The one WorldObject we have at the moment which can attack at the moment is our Tank. To make sure that this turns towards a target properly we need to add some code to Tank.cs.

protected override void AimAtTarget () {
	base.AimAtTarget();
	aimRotation = Quaternion.LookRotation (target.transform.position - transform.position);
}

Here we are working out what rotation is needed to turn the Tank towards a target. To store this we need to add

private Quaternion aimRotation;

to the top of Tank.cs. To actually perform the rotation we need to adjust the Update() method for our Tank.

protected override void Update () {
	base.Update();
	if(aiming) {
		transform.rotation = Quaternion.RotateTowards(transform.rotation, aimRotation, weaponAimSpeed);
		CalculateBounds();
		//sometimes it gets stuck exactly 180 degrees out in the calculation and does nothing, this check fixes that
		Quaternion inverseAimRotation = new Quaternion(-aimRotation.x, -aimRotation.y, -aimRotation.z, -aimRotation.w);
		if(transform.rotation == aimRotation || transform.rotation == inverseAimRotation) {
			aiming = false;
		}
	}
}

Now let’s find out whether the weapon of the WorldObject is ready to fire or not.

private bool ReadyToFire() {
	if(currentWeaponChargeTime >= weaponRechargeTime) return true;
	return false;
}

To track the weapon readiness we need to add

public float weaponRechargeTime = 1.0f;
private float currentWeaponChargeTime;

to the top of WorldObject.cs. This allows us to vary the time taken to recharge the weapon within Unity. The current charge time will be modified in the Update() method (which we will get to in just a minute). If the current charge time is greater than the required time then the weapon is ready to fire.

Once the weapon is ready to fire, it is time to actually use it.

protected virtual void UseWeapon() {
	currentWeaponChargeTime = 0.0f;
	//this behaviour needs to be specified by a specific object
}

Again we will leave the actual implementation of using a weapon up to a specific object. What we do want to do is to reset the current charge time for the weapon back to 0 each time the weapon is used. This guarantees that the weapon needs to recharge after use.

Before we get to implementing the firing of the weapon for our Tank, we need to make sure the Update() method for our WorldObject is adjusted so that we can use the weapon regularly while the WorldObject is attacking.

protected virtual void Update () {
	currentWeaponChargeTime += Time.deltaTime;
	if(attacking && !movingIntoPosition && !aiming) PerformAttack();
}

Here we are increasing the current charge time for the weapon each update. It does not matter if this gets too high, since as long as it is greater than the weapon charge time we are able to fire the weapon. We also want to perform an attack if the WorldObject is attacking but is not also moving into position or aiming at the moment. If you run this now you should be able to see your attack vehicles move into position, although they will not yet be using any weapons.

Use Tank Weapon

Before we finish up this time it would nice to be able to destroy something. To do this, let’s provide an implementation of UseWeapon() for our Tank.

protected override void UseWeapon () {
	base.UseWeapon();
	Vector3 spawnPoint = transform.position;
	spawnPoint.x += (2.1f * transform.forward.x);
	spawnPoint.y += 1.4f;
	spawnPoint.z += (2.1f * transform.forward.z);
	GameObject gameObject = (GameObject)Instantiate(ResourceManager.GetWorldObject("TankProjectile"), spawnPoint, transform.rotation);
	Projectile projectile = gameObject.GetComponentInChildren< Projectile >();
	projectile.SetRange(0.9f * weaponRange);
	projectile.SetTarget(target);
}

Our Tank is going to make use of a very simple projectile weapon. This needs to be spawned at the barrel of the gun (which is now pointing at the target) and sent off to hit the target. We need to make use of the forward vector for the Tank to help us find the position where the Projectile needs to be spawned. To find this I positioned a Tank at (0, 0, 0) and then positioned a Projectile where I wanted it to appear. Since everything still has a fixed y position (on the plane representing our ‘ground’) that remains the same. The x and z positions need to be multiplied by the x and z positions of the forward vector (not the location vector) of our Tank. This gives us the position in the world where we want to create a new Projectile. We are then using the Unity method Instantiate to create the object we want, getting the Projectile object attached to that, and setting the target and range appropriately. The range is set to a little less than the actual weapon range since it was appearing out the far side of the target for some weird reason.

Now, I can hear you thinking “Wait a minute, we don’t have a Projectile yet”, and this is true. But that is something that is easily fixed. First, let us start by creating the necessary scripts. Inside the WorldObject folder create a new CSharp script called Projectile.cs and set it’s code as follows.

using UnityEngine;
using System.Collections;

public class Projectile : MonoBehaviour {

	public float velocity = 1;
	public int damage = 1;

	private float range = 1;
	private WorldObject target;

	void Update () {
		if(HitSomething()) {
			InflictDamage();
			Destroy(gameObject);
		}
		if(range>0) {
			float positionChange = Time.deltaTime * velocity;
			range -= positionChange;
			transform.position += (positionChange * transform.forward);
		} else {
			Destroy(gameObject);
		}
	}

	public void SetRange(float range) {
		this.range = range;
	}

	public void SetTarget(WorldObject target) {
		this.target = target;
	}

	private bool HitSomething() {
		if(target && target.GetSelectionBounds().Contains(transform.position)) return true;
		return false;
	}

	private void InflictDamage() {
		if(target) target.TakeDamage(damage);
	}
}

We assign a velocity and damage variable which can be tweaked inside Unity. These determine how fast the Projectile moves and how much damage it deals when it hits something. We also have a range and a target which can only be set by another object (normally the one that creates the Projectile in the first place). This makes sense, since the Projectile itself does not know how far it can travel, or what it is meant to hit.

The logic of what the Projectile can do is all handled inside the Update() method. First off, if the Projectile hit something we need to inflict damage on that object and then destroy the Projectile. It would be at this point that we would initiate an explosion sequence if we so desired, but I will leave that up to you. Rather than using another variable to keep track of how far the Projectile has traveled we will reduce range as it moves. This means that the Projectile should be moved forward if it still has range left. We need to work out how far to move the Projectile, subtract that value from the range, and then adjust the position of the Projectile. If there is no range left then the Projectile needs to be destroyed (this makes sure that we do not clutter our world with Projectiles which have missed their targets).

To check whether the Projectile has hit something we are going to keep things very simple for now. Ideally we would be checking for a collision with any object in our world. But for now we will just check to see if the Projectile has hit the specified target. This does mean that we will have glitches like being able to shoot through other objects to hit our target. But for now, this will be sufficient. (If we were to detect that the Projectile had hit something else here we could change target to be that object, which would mean that the rest of the code will still work). Dealing damage is as simple as telling the target object how much damage to lose. This does mean, however, that we need to add a new method to WorldObject.cs to handle this.

public void TakeDamage(int damage) {
	hitPoints -= damage;
	if(hitPoints<=0) Destroy(gameObject);
}

Once a WorldObject has no hitPoints left we need to destroy it. Once again, a fancy death sequence would be added here if we wanted it. This is another thing that you can play around with in your own time.

Ok, now that we have code to handle a Projectile we need an object to attach that to. Create a new empty object and call it TankProjectile. Add to this a new capsule with the following properties: Position = (0, 0, 0), Rotation = (0, 90, 90), Scale = (0.4, 0.3, 0.4). I added the Metal material that we created a while back to the capsule so that it looks more like a Tank shell.

To allow us to potentially extend things later on I created a new CSharp script called TankProjectile.cs inside the Tank folder which simply extends Projectile.cs (for now).

using UnityEngine;
using System.Collections;

public class TankProjectile : Projectile {}

Attach this script to the TankProjectile object that you created. Now drag this object down into the Tank folder to create a TankProjectile prefab object that we can instantiate. Next add the prefab to the WorldObjects list in our GameObjectList to allow the code we have above to actually find this prefab when the Tank goes to instantiate a new TankProjectile. I have set the damage for this prefab to 10 and the velocity to 30. This gives us a moderately fast projectile that does light damage. You should now be able to run your game and take one of your Tanks out to destroy some of the ‘enemy’.

And that wraps it up for this time. We now have the ability to tell our Buildings / Units to initiate attack against objects belonging to opposing players. As usual the code can be found at github under the commit for Part 14. I hope that the next part comes quicker than this one did, but unfortunately I cannot make any promises as to exactly when that will be. Until next time, have fun mucking around with what has been established so far!