In these last 5 weeks, I was the programmer for a group project named Titan Tussle (the project that I posted a post mortem about) where I provided about 85% of the script for. below is a link to the project repository and a list of the scripts I made, with an explanation of their function and effectiveness.
These scripts are not an example of my best work, but more an example of what I am capable of under adverse conditions with minimal time available.
link to the titan tussle repo
AudioBehaviour
This was set up to behave as a single game object that would contain all of the audio sources in the scene. These audio sources would be kept in lists, and used pooling to avoid creating too many audio sources at once, or the need to destroy ‘one shot’ audio sources as I had in previous projects.
This script contains a public function to be called by any other script when they need an audio clip played, asking for an index range (and a bool for looping the sound, which did not end up making it in).
This function picks a random number in this range to find the index number for the desired clip, accessing the first level of a 2D list of audio sources with it. It iterates through that list, looking for the first audio source that isn’t currently playing and tells it to play. If it fails to find one in the list, it creates a new audio source component, sets its audio clip to one found in a list of audio clips using the same index, sets its volume and adds it to the 2D list, telling it to play.
This method removes some overhead with garbage collection, moves all the audio into one place, allowing easy control of each sources’ variables (like reducing the volume of each clip created in the same frame, or within a set time of the previous one, or applying a global volume level). I chose this method as I was not aware that you could play multiple clips from the same audio source component without them cutting out the currently playing clip.
It is not easy to use, however, as if you want to add a new clip into the middle of the array of clips, you move every clip after it down the list, requiring you to update every index range in every other script that calls on this, which is hard coded. This makes it difficult for use by anyone who did not make your script, or the project team whenever the audio list is updated.
The list of audio clips and the issue of adding in new clips forcing a remap of all the calls to this script could be avoided through using another 2D list, requiring only a single index number, placing the multiple possible files into these lists and the use of “unity typesafe” to automatically make available any new audio asset in script, and add them into these lists on play. Any audio dropped into an appropriate folder would then be placed into the audio clip list already in use, the random range would include it in its roll and no index numbers would need to be updated. Through use of strings as indexes, you would not even need to be aware of the correct order of the audio folders, just the name of the folder, easing use for both programmers and designers using/calling this script.
ButtonManager
This contains the behaviour of each of the title menus and the scene camera. I wanted to add something fancy to the title, so decided instead of fade in/out of canvases or scenes, or just changing the displayed text, to have canvases displayed in world space and move the camera itself to each new location. This has three states, one for each canvas, in which the controller inputs do different things. The title and credits just change the camera’s position, but the ‘pregame behaviour’ (or character select screen) is set up to help each player determine which character they are controlling, by asking each player in turn to confirm readiness. As they do, an icon of their titan (bordered by their team colour) is brought to full opacity. This did not work very well, as every player would mash their buttons at once. This could be fixed by instead allowing players to select which titan they want to play as, removing the possibility of all players just hitting the same button, and would allow players to form their own teams.
CamelBehaviour
A fairly simple script, It keeps track of player positions, finds the closest player and after a random time in a range of 1-10, adds force to itself in the opposite direction, playing an audio file when it does.
It doesn’t behave much like a camel does, as camels don’t normally launch themselves 50 meters into the air.
This should have instead used a navmesh agent or at least tried to move towards a created waypoint, and I could have asked for a walk cycle animation for the model.
CameraController
This carries a camera shake function, object fade and also keeps the camera focused on the center of the action.
The camera movement worked by keeping a list of the players and every frame calculating the distance between the furthest apart characters, by iterating through each character, determining its distance from each other character, adding that distance to a list and then sorting that list.
It also found an average point by adding each of the character positions together and dividing that position by the number of characters.
The average point was used as the camera’s position, and the camera was moved backwards by the max distance (clamped to a minimum and maximum) which was applied to the camera using linear interpolation.
This used to have the camera rise up with distance and rotate to look at the average position also, but created an issue where the titans looked very small and were hard to track as the camera moved further out (ending up looking down at the scene).
The object fade is a trigger for the operation in the obstacle script where a ray is drawn from the camera to each character, and if it collides with an obstacle on the way, runs its fade out function in its obstacle behaviour script.
The camera shake is run each frame if the amount of shake desired is above 0. the desired amount is reduced by Time.deltaTime every frame and is clamped to a maximum amount. The camera transform is moved by a random range of -desired amount to +desired amount, in the x and y-axis. The desired amount is added to by calling the public function ShakeCamera(desired amount). This function is simple, the desired amount is easily tweaked, it doesn’t run when it is not needed and was simple to put together and use.
In its entirety, the script is simple and effective, without being too expensive. It is using tags to find each character and stores a reference of them in its own list, but it does not search for these on start, instead requiring the game controller script to call its FindCharacters() function after it has completed Instantiating them, to avoid getting null references and allowing these characters to be removed or added into the game.
CharacterControl
This script is messy. It controls all the different actions and behaviours each player can take with each type of character (Large and Small titans). It keeps the player number info, finds and stores the correct gamepad device, Displays the character direction and attack UI element (displayed in world, not on a canvas), handles changing the character’s material colour and sets triggers in the characters animation controller.
It contains the different movement and attack behaviours for each type of titan. The large titan can only move through attacking, but can always rotate. this rotation speed increases over time while the attack button is held down. The target UI plane is turned on at the beginning of a charge and turned back off once an attack is released. The attack itself uses transform.translate to move the titan by its move speed towards the target position, which has an issue with overshooting or getting stuck, so there is a timed end to the attack, and a range within the target that will also end the attack, however, these issues still stand. The charge overshot could be fixed by just using a timeout based on how far we want to move, and getting stuck on objects could be fixed by ending a charge if our position has not changed by a certain distance since the last frame.
The small titan has the ability to move and attack. The movement went through three iterations, first strafing forwards, back, left and right with the left stick and rotating by pulling left or right on the right stick, which had the problem of being too slow and confusing. This was changed to a geometry wars style, where you still strafed with the left stick, but changed facing direction with the right stick. This was less confusing and responded much quicker, but meant that when the animations came in, you could face in the opposite direction you were facing. The controls were then changed so movement and facing direction were controlled by the left stick, so you were always moving forwards, which lined up with the animation. This allowed the use of the right thumb to attack while still being able to position the titan, also conforming to the control scheme of the large titan, easing the use of both.
The small titan attack performs an overlapSphere operation directly ahead of it, ignoring itself and calls the stun function in this script on any affected titan characters (checking for tag).
The small titan also has the ability to collect a BigShot powerup. If they have collected it, and attack, theyInstantiate a prefab projectile in front of them, and turn off the UI to display that they have the powerup.
This script also handles the collision behaviour of the titans, mainly calling the match controller script when they fall out of bounds, hitting and destroying obstacles, hitting other titans while stunned and the large titan charge attack collision behaviour (adding force to another titan in the opposite direction of the collision).
Other than the issues with the charge attack overshooting, this script also has issues with the triggers for the animations. These trigger timings cause the animations to not seem very smooth, either playing the animation too late or flicking between several at once and also have a problem of getting stuck into them. This script could have been set up as an FSM, which could have avoided a lot of the messiness of this code and made it easier to fine tune the animation transitions.
DustBehaviour
This script was set up as an alternative to a particle emitter, as I could not place a sprite animation sequence into the built in unity particle emitter system. This setup uses a rigid body component to have gravity act upon it, billboards the object, interpolates the colour to clear/black and destroys the game object after a timer hits -3. This is a very expensive way to handle this, as the obstacle Instantiates one of these every frame for ~3 seconds as it is destroyed, creating around 120 new game objects over that time, adding force in a random direction, which are then destroyed 4 seconds later, generating a fair amount of overhead through garbage collection.
This looked pretty nice with the animated particle, but they were also fairly small and hard to see the animation during gameplay.
This should have instead used the pooling method, with an effects controller to store the pool. It should also not have used a rigid body component, and instead followed a bunch of adjustable curves over its lifetime.
GameController
This script handled the spawning of the titans at the start of each match, figuring out the appropriate model and behaviour to be used for each spawned titan. After completing this, it calls the camera control script, asking it to find the titan characters to begin following them.
This function doesn’t do too much but was planned to be used to carry across information on which player chose which titan (when the plan was to have multiple available) and which map to use.
MatchController
This keeps track of team ring outs, holds the match timer and respawn timers, removes sections of terrain after enough time passes and handles the end of the match. It used to attempt to fade out a character’s material once it had hit the boundaries of the arena and ‘died’, but using the new shader system in unity 5 does not allow a solid shader to fade out to transparent. This could have just been switched from opaque to fade through code, though, this approach would also be useful for the obstacle fade behaviour.
It also changes the text in the UI, including the displayed match timer, winning and match information text and each team’s points towards winning.
This, along with most other scripts here, makes heavy use of ‘findGameObjectsWithTag’, which I have been urged not to use as it is very easy to break this functionality by changing the tags or just misspelling the tag in the script, which has no auto complete to help remove these mistakes. I have since found out that using ‘findGameObjectsOfType’ is a simple way to work around this, with the added bonus of not needing to play around in the editor to complete the connection of these scripts.
ObstacleBehaviour
This handles the obstacle’s behaviour when it is destroyed, where it slowly falls down through the ground, spawning the animated dust particles, playing sound based off if it is a building or camel and also has a fade out function, to fade the model to 10% opacity when it is obscuring the camera’s vision of a titan.
The game object is destroyed after its health is reduced to 0 (purely so the pyramid is not instantly destroyed) and a timer is reduced to 0. While the timer counts down, every frame, a dust particle is instantiated, and force is added in a random direction and strength. This is also done in fixed update. This is the messiest and least efficient way to create an already expensive game object, The least that could be done here would be to create a variable that controlled both the amount created, over a set time and to give the option of ‘bursts’ as are found in the particle emitter system.
The fade out function is not very good, every frame that it is called, it sets a bool to true, which it then checks if it is true, and if so, fades out (from its material’s current colour to clear), if not it fades back in. It then sets the bool back to false. On my laptop, this worked fine, but during playtesting it caused the buildings to flicker at a high frequency. The easiest fix would be to apply a half second timer to the bool being set back to false, but this doesn’t really remove the problem, just hides it behind a timer. A nicer solution would be to have a fade in and fade out function to be called, where if the function to fade in is not being currently called, the camera script, upon its ray cast no longer hitting this game object, would call the fadeIn function.
ProjectileBehaviour
This handles the projectile’s movement, causes camera shake, lowers the scene’s main light intensity and behaves like a large titan charge attack with increased strength. The collision behaviour was just copied from the character control script, as I knew that already worked. The movement is handled using move position to help avoid tunneling. The projectile lives for 7 seconds, lowering the light intensity for the first 2.
I have not come across any issues with this behaviour yet, but as it has such a small time to exist in the game, it is hard to test for bugs. It does cause issues with characters tunnelling through the ‘death box’ boundaries of the arena though. This could be avoided by hard-coding the boundaries.
UIContainer
This was a container created to be used in a prefab, for the MatchController to grab the appropriate UI text from.