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.

Advertisements

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!