Objective: Implement a pause menu and the possibility to quit the game. Extensive use of Animator states. Basics of pre-processor directives.
This tutorial is going to be hard, but cool and full as well. The creation of a pause menu will allow us to put many ingredients on the table and learn a lot along the way.
The idea is to create a menu, very similar to the main one, hidden to the view (outside the screen). When the escape key is pressed, the menu will slide down and the game will pause.
Once in this state, it will possible to undo everything by pressing again the escape key or using a “resume” UI button. A “quit” button is also there and will stop the game at all.
The creation of the menu is not anything you can’t do at this point, let’s just revise the result.
As you can see, there is a background image and two buttons, all children to a PauseMenu empty game object, the latter child to the canvas. The starting position is above the screen at y=1536.
Now, we need a sliding down animation. Click on the PauseMenu, open the animation view and add a new animation. Instead of loading frames/sprites, this time we will use the record button on the left to record movements as an animation.
Click on Add Property button and select Anchored Position. This is tell Unity that the animation will involve that property. With a right click on the right area add two key frames, one in 0:00 the other in 1:00.
On the left bottom area, click on Curves tab. This is a powerful instrument!! IT will show 2 lines, in this case a red one for the x value and a green on for the y. What we have to do is to drag the green (y) line in 1:00 frame down to zero value. Unity will provide a smooth curve as you can see. What’s the meaning?
Well, the curve is exactly the graph of y value, function of time. Between 0:00 and 1:00 (I believe one second), the y position will go from 1536 to 0 following that curve. Click on record button again to finish.
It is not over, since you have to do the reverse, from 0 to 1536, in the exact same way. After finishing, if you go on the Animator view, you’ll see the two animation as separate states. Let’s start our orchestration!
As already done before, a new default empty state is needed, I called it hidden. From hidden a new transition will lead to slide-down animation, from there a transition to slide-up animation and eventually from there to hidden again.
Let’s add a new trigger parameter, onPause. Transitions hidden->down and down->up must be dependent on that parameter, while up->hidden is empty or automatic.
Cool. We’re done. Let’s imagine now:
- The player press Esc and the menu slides down (hidden->down transition), game is paused
- The player somehow resumes the game, the game continues as the menu slides up (down->up tr.)
- The menu stays hidden (up->hidden tr.)
We need now to code and script this behaviour to make it dependent on player input.
First of all, let’s learn how the pause is actually implemented. In a Unity application/game there is a consistent concept of (almost actual) time. We already used Unity Time class to get the time interval between a frame and the next one, but many other features are available. Among them, we’re going to use Time.timeScale which is a sort of multiplier of time flow: the grater the value, the quicker the time flow. So, you can set it 0.5 for a slow motion…
… or you can use 0 value to stop time and 1 to make it flow normally.
This utility method in UIManager Script will detect the PauseMenu position (while sliding up or down) and pause the game when it is down, resuming it when it is up again. As simple as setting the timescale value.
This is the actual behaviour. This method will be put in Update(), it will check for player input: when the key is pressed the trigger is set, and we just need this! That is because we set all those transitions between the states so the actual effect of triggering depends on the state in which we are! I can’t help but find it cool!
You can even play with those y values to pause the game at your preferred menu position.
The time scale method is then called, it will occur at each frame. It is ok since the effect is dependent on the position of the menu and will do exactly what we planned.
But it doesn’t work.
It cannot work.
We stopped time!! How can an animation or anything else work if there is no time flow?
Well, it can’t in the real world, but here we have power: reach out for the Animator component of the PauseMenu and change the Update Mode to Unscaled Time. In this way, this animator will ignore the timescale and the animations will go on even if the rest of the world is frozen.
Are you worried about those hard coded position values? Or about the possibility of screw everything if the camera zooms in or out?
So was I… but keep calm and think: the menu is not a real game object, it lives on the canvas! The position we are talking about is actually the anchored position of a rect transform (not just transform) component. Absolutely independent of game world position.
We are not done yet. We have two buttons to manage: resume and quit.
The resume functionality is just what would happen with the pressure of Esc key.
The quit functionality is just a bit more complex. Unity provides an east Application.Quit() method which will close the game… the built and deployed game! This means that if we run it through the editor there will be no effect. Here is the workaround.
The statements starting with # are pre-processor directives. These directives tell the compiler what to compile and what to ignore, before compilation itself!
So, before compiling, there is this check: are we in UNITY_EDITOR? Yes: compile that part of code, so quit the game by setting the editor as not playing. The compiler will totally ignore what’s under #else. Otherwise, if the answer is no, the first part won’t be compiled at all, and only the second will. The #endif states the end of the part which is subject to pre-processor check.