With the inclusion of Obstacles; there isn’t anything else I really wanted to add to this project.
It still needed a lot of work; pulling all the elements together and polishing the presentation – One final Tighten & Tidy.
But I felt I’d done what I set out to achieve at the start.
I’ve included notes for the larger updates and amendments below – & while there’s a lot more that could be done (especially with the code structure) – I think this will be it for project 1.
Particles – This is a very simple game, and could sometimes look a little flat. I added a small number of collision particles (platform collision, pickup collision, obstacle collision) to hopefully make the game more visually appealing.
Standardised Colours – For better or worse I’ve used the same colour scheme for all my games since EOL back in 2010.
To standardise the same colours across the game I created a script called ColourManager. This contains a number of static methods to manage element colours;
GetColour returns a specific colour, used by HighScoreAdd to manage the keyboard key colours.
SetColour used to set the colour of a components renderer. Used to set the colours of platforms, obstacles and pickups
CurrentColour & NextColour used by platforms and titlescreen text to loop through each colour in rotation.
Ensure player isn’t killed at game start – Until now, at the game start; a player would sometimes immediately hit the edge of a platform and fall out of the game – Ending the game before it had begun. To fix this I made a very minor edit to the PlatformManager code so that the very first platform is much longer – Giving the player a safe entry to the game before different platform heights are introduced.
Ensure player managed when not in-game – On the title screen (and game over screen) the player components ridgedbody element was still being updated – If a player left the title screen running for any length of time, they would be subjected to mysterious platform colour changes & collision sound effects being played seemingly randomly. To resolve this, I now only update the player if in-game (if statement in the players Update method) – Otherwise I reset them to an offscreen position and reset the velocity
void ResetPlayer() { rb.transform.position = new Vector3(0, 7.5f, 0); //Set game init position rb.velocity = Vector3.zero; }
Set Alternate Game Name – Changed the title screen to say 20 goto 10 presents Bock Run. It’s not a great name, but seemed a little better than Welcome to Test Game (Colours are randomly determined on screen init).
In-Game Scoreboard – I simplified the presentation of the scoreboard by only displaying the players score. Though I wanted the score to be shown behind the player (but in front of thr skybox), so I complicated the back end. Adding a new canvas (called CanvasScore), plugging in the Main Camera changing the render mode to ScreenSpace – Camera
SkyBox – I liked the default skybox, but I thought I should at least try something a little different. Ultimately I settled on the Fantasy Skybox FREE by G.E.TeamDev – Stripping out all the content except the Sunny 01A material and Sunny_01A_Front texture.
Player Death Pause – Colliding with an obstacle, results in the player exploding in a cloud of particles – game over. Previously, after creating an instance of explosion particles the game went straight to either the ‘new highscore‘ or ‘game over‘ screen letting the particles play out in the background. While this didn’t look too bad, I wanted to add a slight pause – Long enough to let the player appreciate what had happened – But not so long as to be distracting – I guessed about a second would do the job.
My quick and dirty way to add this, was to create a new game states ‘GameOverPause‘ which would be set only if the game over state was a result of obstacle collision. When setting ‘GameOverPause‘, a timer is run as a coroutine;
IEnumerator GameOverPauseTimer() { yield return new WaitForSeconds(1); HighScoreCheck(); }
Once the timer completes HighScoreCheck() is called and the games next state is detemined – ‘New Highscore’ or ‘Game Over’
Background scrolls smoothly when transitioning from game over to game start – At the moment, when a new game is started, I reload the scene – start everything from fresh. I wasn’t sure if this was a little jumpy & liked the idea of the background blocks continually scrolling, whatever the game state (game/game over/new highscore, etc…). No reset or reloads – when the game ends, the background keeps scrolling – when the player presses play they fall straight into the scene whatever state it’s in. I ultimately decided not to go this way (Because of ‘Ensure player isn’t killed at game start‘), but the code is still in place & can be activated, either in-code by setting the ‘bool reloadLevel‘ (in the GameStateManager class) to false – Or in-game by pressing ‘R‘.
Multiple obstacle types – spheres bounce, blocks burst – Expanding obstacles a little – As well as the instant death obstacles, I added new type – using the ObstacleSphere prefab – bounce. Both obstacle types use the same script, the only change is the addition of a enum used to determine obstacle type;
public enum ObstacleType { Cube = 0, Sphere, Pyramid } public ObstacleType type;
& a little code to handle the different collision responses – Cubes & pyramids use the old/existing – instant death code – While spheres ‘bounce’ the player uncontrollably
void OnCollisionEnter(Collision collision) { if (GameState.Is(GameState.State.InGame) && collision.gameObject.CompareTag("Player")) //Ensure we are checking aginst the player. { Player player = collision.gameObject.GetComponent(); //Grab the player if (type == ObstacleType.Cube || type == ObstacleType.Pyramid) player.PlayDie(); else if (type == ObstacleType.Sphere) player.Bounce(); } }
Bouncing just increases the players Y velocity;
public void Bounce() { float bounceAmount = 6f; rb.velocity = new Vector3(rb.velocity.x, Mathf.Abs(rb.velocity.y) + bounceAmount, rb.velocity.z); audioBounce.Play(); }
Controller support – Support for different input methods was pretty spotty – some screens recognised mouse and keyboard input; others only mice. – I wanted to ensure all screens could be controlled by either keyboard or mouse, as well as gamepad (No touch input yet though).
The setup as it currently stands is a little rough, clearly a first draft which could benefit from a review/rewrite or two. It’s a static class based around four methods – Two methods Horizontal & Vertical check for player movement requests & two methods ButtonsDown & Buttons to check for player action requests (jump, select key, button, etc).
Companion axis methods Left/Right – Up/Down – HorizontalOff/VerticalOff use the results of Horizontal & Vertical to provide reference to the current state (e.g. is the player trying to move left) without needing to rerun Horizontal & Vertical more than once a frame. Used to navigate the the virtual keyboard and main menu buttons.
Companion button methods e.g ButtonADown, ButtonBDown, ButtonShoulderLDown are used to reference specific button selections.
Horizontal() & Vertical() effectivly both work in the same way, just along different axis. They are run every loop; checking for user input using a valid method (left thumbstick, right thumbstick, arrow key, etc) and return a result value; -1 for left/down, 0 for no input & 1 for right/up.
public static float Horizontal() //Is the player using horizontal movement { lastHorizontal = currentHorizontal; if (Input.GetKey(KeyCode.LeftArrow) || ZoneCheck(ZoneRange.Left, Input.GetAxis("Horizontal")) || ZoneCheck(ZoneRange.Left, Input.GetAxis("HorizontalThumbstickR")) || Input.GetAxis("HorizontalDPad") 0) currentHorizontal = 1; else if (Input.GetKeyUp(KeyCode.LeftArrow) || Input.GetKeyUp(KeyCode.RightArrow) || (currentHorizontal != 0 && ZoneCheck(ZoneRange.Centre, Input.GetAxis("Horizontal")) && ZoneCheck(ZoneRange.Centre, Input.GetAxis("HorizontalThumbstickR")) && Input.GetAxis("HorizontalDPad") == 0)) currentHorizontal = 0; return currentHorizontal; }
Buttons() checks all valid input methods (gamepad buttons, space bar, return key, etc) using the unity function GetButton – To check if the button is currently being pressed -Used for in game jumping.
public static bool Buttons() //Any button pressed { if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.Space) || Input.GetButton("JoystickButton0") || Input.GetButton("JoystickButton1") || Input.GetButton("JoystickButton2") || Input.GetButton("JoystickButton3")) return true; return false; }
ButtonsDown() checks all valid input methods (gamepad buttons, space bar, return key, etc) using the unity function GetButtonDown – To check if the button was pressed in the last frame. Used when the user selects a button from the Main Menu.
public static bool ButtonsDown() //Any button pressed { if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.Space) || Input.GetButtonDown("JoystickButton0") || Input.GetButtonDown("JoystickButton1") || Input.GetButtonDown("JoystickButton2") || Input.GetButtonDown("JoystickButton3")) return true; return false; }
Split GameController into multiple classes – The GameController script was heavily overloaded – It managed the games states, title screen buttons, gameplay loop, player score, game timer, scroll speed, in-game music. While I suspect it really needs to be completely re-worked, for the moment I spread the functionality over three classes;
GameState – Really just holds & manages an enum used to determine the games current state (title screen, game over screen, in-game, etc) and the games last state.
GameStateManager – Is still rather overloaded since it does a lot more than just manage the GSM – Looks after the title screen buttons, manages highscore add, game over pause – Pretty much everything that isn’t ‘in-game’
InGameManager – Does handle everything in game (platforms, obstacles, player, etc) – It also manages the players score, background scroll speeds, etc.
It’s far from idea & both GameStateManager and InGameManager are still overloaded, but I think it is an improvement.
Instructions – I added some very simple instructions -To let players know what they can do (e.g. double jump) and how they can do it (keyboard mouse, gamepad). I try to keep instructions as simple as possible – & as free of words as possible – In case the player doesn’t speak English.
GamePad icon made by EpicCoders from http://www.flaticon.com
Mouse icon made by Freepik from http://www.flaticon.com
So, for version 1 at least, I think that’s pretty much everything. It’s far from perfect, but for my first attempt at a Unity game – not a complete failure – I’ve some very basic gameplay, a messy GSM, a working online highscore table and a much greater understanding of Unity.
As usual, a video showing the game, a link to the source code and a link to play the game online (WebGL) can be found below.
Thankyou!
Grab a copy of the project here
Last post: 1.8 Obstacles
One Reply to “Adventures in Unity – 1.9 Tighten & Tidy”