Small Personal Project

Over the weekend when I got a little bit of time to myself I thought I would pick up an old challenge I set myself a while ago that was stopped by University work. The challenge was simple enough and revolved around Blizzards game Overwatch. Overwatch is a team based fps game with unique characters, each one of these characters will have their own abilities that allow them to get an edge in combat.

Since Overwatch’s release I had set myself the challenge of remaking each character within the Unity game engine, and over this weekend I found myself a few hours to begin remaking the crowd favourite, Tracer. Tracer’s two main abilities are:

  • Dash – Moves tracer in the direction she is facing a set distance
  • Rewind – Moves tracer back along here previous path a set distance

So to begin I created a fairly simple character object with a controller that I would then attach my control script to. This control script is a generic script set up to allow movement of any character. Below I have listed the function to actually move the player character, this function is called from within the fixed update.

	private void MovePlayer(){

		//Set up initial values for movement
		Vector3 desiredMove = new Vector3(Input.GetAxis("Horizontal"), 0.0f , Input.GetAxis("Vertical"));

		//Copied from Unity standard first person controller
		RaycastHit hitInfo;
		Physics.SphereCast(transform.position, playerController.radius,
							Vector3.down, out hitInfo,
							playerController.height/2f, Physics.AllLayers,
							QueryTriggerInteraction.Ignore);

		//Rotate the vector that will be moved along by the camera rotation
		desiredMove = Quaternion.AngleAxis (characterTargetRot.eulerAngles.y, Vector3.up) * desiredMove;
		
		//Assign the x and z components
		forwardMovement.x = desiredMove.x*movementSpeed;
		forwardMovement.z = desiredMove.z*movementSpeed;

		//Check that the player is currently on the ground
		if (playerController.isGrounded) {

			if (Input.GetButtonDown ("Jump")) {

				forwardMovement.y = jumpSpeed;
			}
		} else {

			forwardMovement += Physics.gravity * Time.fixedDeltaTime;
		}
		//Move function founf as part of the character controller
		CollisionFlags temp = playerController.Move (forwardMovement * Time.fixedDeltaTime);
	}

The movement is only the first step of the character control though as you also need to factor in the camera movement this is achieved in the function below.

	private void RotatePlayer(){

		//Set rotation values using defined sensitivity
		float yRot = Input.GetAxis("Mouse X") * XSensitivity;
		float xRot = Input.GetAxis("Mouse Y") * YSensitivity;

		//Set character target rotation
		characterTargetRot *= Quaternion.Euler (0f, yRot, 0f);
		//Set camera target rotation
		cameraTargetRot *= Quaternion.Euler (-xRot, 0f, 0f);

		//Call the clamp function
		cameraTargetRot = ClampRotationAroundXAxis (cameraTargetRot);

		//Set each rotation
		this.transform.localRotation = characterTargetRot;
		PlayerCamera.transform.localRotation = cameraTargetRot;

	}

The ClampRotationAroundXAxis function for those who are interested is just reusing the clamp function found in the move camera script that can be found in the Unity standard assets. However I will include the code here for reuse.

 	Quaternion ClampRotationAroundXAxis(Quaternion q)
	{
		q.x /= q.w;
		q.y /= q.w;
		q.z /= q.w;
		q.w = 1.0f;

		float angleX = 2.0f * Mathf.Rad2Deg * Mathf.Atan (q.x);

		angleX = Mathf.Clamp (angleX, -90f, 90f);

		q.x = Mathf.Tan (0.5f * Mathf.Deg2Rad * angleX);

		return q;
	}

Now to move onto the main point of all of this recreating the abilities. To set up the controller for abilities I decided to use a bit of composition. I’ve illustrated below with a very basic UML (that I hope I’ve done correctly).

uml-characterabilities

With this method I am able to easily swap between “ability set’s” which eventually once weaponry is added in a similar fashion should allow me to mix and match character traits for some silliness.

So let’s start with the dash ability that tracer has, this ability was simple to implement however could do with some refinement in the future. The code is listed below for those interested.

	//Dash Ability
	public override void triggerAbility2(){

		//Set up initial desired movment for forward only
		Vector3 desiredMove = new Vector3(0.0f, 0.0f, 1.0f);

		//Rotate the movement accordingly
		desiredMove = Quaternion.AngleAxis (this.transform.localRotation.eulerAngles.y, Vector3.up) * desiredMove;

		RaycastHit hitInfo;
		//Raycast in front of the player in the direction of the dash
		if (Physics.Raycast (this.transform.position, desiredMove * dashSpeed, out hitInfo, dashSpeed)) {
			//if there is a collision move the player to the collision point
			this.transform.position = hitInfo.point;
		} else {
			//if not dash normally
			this.transform.position += desiredMove * dashSpeed;
		}
	}

The rewind ability is a bit more complex than the dash ability as it has to record the players movement over a fixed period of time then move backwards through that recorded input. To record the movement I was originally thinking of using array’s a vector or maybe one of the list options given by unity, in the end I decided on a queue. To get a queue to work I just had to add position to the queue at each update step (and remove the oldest if the queue got too big) as shown below.

	void Update () {

		if (!rewinding) {
			//Add position to queue
			rewindQueue.Enqueue (this.gameObject.transform.position);

			if (rewindQueue.Count > rewindTicks) {
				//remove oldest position from queue if the queue is large enough
				rewindQueue.Dequeue ();
			}

			rewindCount = rewindQueue.Count - 1;
		}
	}

So now that I’m recording position how do I cycle though it all as I can’t take from the start of the queue only the end. After looking around I found a solution that was a little odd but I liked. When the player activates the rewind ability the entire queue is pushed onto a stack as shown below.

	//Rewind Ability
	public override void triggerAbility1(){

		int count = rewindQueue.Count;

		if (!rewinding) {
			for (int i = 0; i < count; ++i) { 				//move onto stack each queue element 				rewindStack.Push ((Vector3)rewindQueue.Dequeue ()); 			} 			rewinding = true; 			StartCoroutine (rewindAbility ()); 		} 	} 

At the end of that function you can see that there is a call to the StartCoroutine function. Coroutines are essentially small functions that will work in their own time that you can specify. In this case I want the player to retrace their steps at a constant rate shown below. I am moving back two position each run through as I seem to have found an upper limit to how quickly a Coroutine can be called and had to improvise.

 	IEnumerator rewindAbility(){ 		//Remove player control 		playerMovement.setControl (false); 		//Start to loop through the stack 		while (rewindCount > 0) {

			this.transform.position = rewindStack.Pop();

			rewindCount--;

			this.transform.position = rewindStack.Pop();

			rewindCount--;
			//wait for fixed time before continuing loop
			yield return rewindWaitTime;
		} 

		//return player control
		playerMovement.setControl (true);

		rewinding = false;
	}

Once we add all these pieces together we get the result shown in the video below:

Overall I think it’s a good start and hopefully I will be able to continue it soon.

My Roles on HexWorld

When we started working on HexWorld none of us really knew what we would end up doing for the project as we’d never done something like it before. Over the course of the project our roles would become more defined instead of everyone being “General Programmer”.

Towards the end of the project my roles had moved into the UI and Sound categories. With Unity, or at least the version we were using creating a menu system and a sound management system seemed like a fairly simple thing however as time went on I encountered some rather unusual issues with creating both.

UI

For the UI of HexWorld I needed to create the management system and art. With the artwork I kept it fairly simple with pixel art images created in Paint.net that I then adjusted inside of Photoshop. Below I have included images of some of the artwork:

This slideshow requires JavaScript.

The problems when creating the UI lay in the management system, the whole system was supposed to use Unity’s inbuilt event system. As time went on the whole system started to act rather strangely with one of the largest problems being dropped input when dealing with controllers, and broken function calls (despite what was being told to me by Unity). After trying to fix these issues for a long time it was decided that it would be easier to just make my own simple event system.

In creating this simple event system as a proof of concept a selection of switch statements was created that would manage the different layers of menus that existed. This worked first time with no serious issue surprisingly, the only real issue with it was that it couldn’t be changed for the finals of the Imagine cup due to University work. If I had the time to change it I would have likely used a state machine to make expanding it much easier.

Sound Effects

As I was the only person in the group with any experience editing audio files (worked with a friends band as general “Sound Guy”) I volunteered to try and bring some audio to the game. The sound effects themselves were created using open source tracks and a lot of editing inside Audacity to get the right sound which you will be able to find some of here (the jump pad is mine and Riccardo’s favorite, and also took way to long).

The actual manager of the audio changed in approach a few times during development. Originally it started life as adding audio sources by hand to the objects that will create them. This approach quickly proved to be terrible especially when we realized all of the hexagonal tiles would require there own audio source.

After scrapping this manual method an automatic manager was created. This manger was designed as a fire and forget tool that would take in information of the object producing audio, and the audio to be produced. The manager would then bind an audio object to the producing object with the correct sound file attached and then pass a reference to the producing object that linked to the audio object. The audio objects themselves would clean themselves up if no sound was playing from them for a period of 0.5 seconds. When tested this method proved to be very effective and pats on the back were given before we firmly slammed into a wall and everything broke again.

The reason everything broke was due to the sheer number of audio sources that were being created in one of our test scenes. As it turns out you can’t just expect to have an unlimited supply with no issues. When in the test scene over 1000 audio objects needed to be added to the scene which as it turns out exceeds the maximum number of audio objects supported by Unity…….on that system. As it turns out there isn’t a set amount supported as it’s system dependent and there is no way of finding that number (or at least no way I could find) without breaking it.

After testing on multiple systems we found 500 to be a safe number of audio sources. With this new safe number intact I changed the system to never exceed that upper limit with over half of the limit being separated for tile movement. In order to ensure that only the tile closest to the player would play audio a simple distance check was created that would run before the assigning of audio sources that would cancel if they were too far away.

Summary

There is more of my work in HexWorld than what is above however it is hidden among other team members code or I have just forgotten that I had written it. If you would like to see an example of any of the code I have talked about feel free to contact me.

Imagine Cup World Finals

We were out in Seattle for 5 days on what is one of the best trips I’ve ever been on. During our time there we had late nights, horrible jet lag, met some old friends, made some new ones, and got to enjoy the hospitality of Microsoft.

Day 1 – Into the Void

Our journey to Seattle was long and completely destroyed our internal clocks as we left Cardiff at 6am on the 25th, traveled for around 14 hours, then arrived in Seattle at roughly 11am. After departing the airport we traveled to where Microsoft had given us accommodation at the University of Washington. After getting lost a few times we got into our rooms dropped off our bags and went exploring, this gave us a chance to get our bearings and discuss our pitch. One thing I feel I must mention here is that the University of Washington is gorgeous, and I kept forgetting to take pictures so I’ll be using some stock images.

uow

Day 2 – Getting Ready

On the second day there we started off the day by getting all of our event items such as Passes, Schedule, and T-shirts. On the schedule for the day was interviews with film crews, some small photo shoots, and the pitch dry run where we would be critiqued on our pitch to try and improve it by the next day. After performing our dry run we were given some helpful advice which caused us to change a significant portion of our pitch and create a trailer. One thing that struck me about everyone we met was that everyone was super friendly, and as a group we express excitement very poorly which seemed to confuse the interviewers slightly.

imagien-cup-group-photo

 Day 3 – It’s All Lead to This!

Our third day was the big day the day we would pitch our game to the panel of judges. After very little sleep and travelling to Microsoft’s Redmond Campus we readied ourselves to be the first team up. We were to do a 10 minute pitch followed by a 20 minute Q&A, we were terrified (or maybe that was just me) but we handled ourselves well with few hiccups.

imagine-cup-presentation

After our pitch we were interviewed for a live stream by the event manager of the Imagine Cup which was fun. We then went to explore the visitor center of the Redmond Campus and meet some of our friends to discuss the pitch. Riccardo and Myself had to leave the group after a bit to attend Microsoft’s HoloLens academy. The academy was great fun and the HoloLens is one fantastic bit of technology that I’d honestly love to have at home to play with.

HoloLens Riccardo.jpg

Day 4 – Showing the People

On the fourth day in Seattle we were to find out the top 3 teams of each category at an event attended by Satya Nadella. Sadly we didn’t make top 3 but in the end that didn’t matter, we had an amazing time creating HexWorld and went further with it than we ever thought we could.

After the award ceremony we traveled to Microsoft’s exhibition where everyone was to show off their imagine cup projects. This lasted a few hours and we met a lot of people, got to try some cool projects, and destroyed our throats because of what we nicknamed the Welsh Plague (it was just a throat infection).exhibitition-1

Day 5 – The End is Near

On the last day in Seattle we traveled to Garfield High School (which excited me greatly as it’s where Jimi Hendrix was taught), whilst there we watched the final showdown between the top teams of each category. Afterwards we were essentially free to explore Seattle before a final party for everyone was thrown.

group-photo-1

Overall this was a one of a kind experience that helped us meet new friends, see old friends, and visit one amazing place. If I could do it again I would sadly having graduated I’m no longer eligible to join the competition but I’ll never forget it………..especially with this horrifying panoramic I took where my brother turned his head between shots, I really am bad at taking pictures.

panoramic

 

Imagine Cup 2016

sapient_team

I am currently one quarter of the indie development team Sapient. Team Sapient consists of (In order of left to right in the image above) Me, Michael Harty, Riccardo De Luca, and Thomas Mahoney.

Over the past year we have been working on a game that we have entered into Microsoft’s Imagine Cup. With HexWorld we have won the Imagine Cup national finals for the United Kingdom, made it through the semi final round, and will soon be competing against several other teams in Seattle in the world finals. We will be flying to Seattle on 25/07/16 to compete in the week long competition finals.

To find out more about the Imagine Cup, Sapient, or HexWorld please visit the links provided.