Beginner, Tutorials, Uncategorized, Unity3D

Unity3D Animated Pause Menu, Part 4: Responding to Player Input

Now that we have a functioning animator controller, it’s time to accept player input so that we can actually pause the game and bring up the menu.

To do this, create a new C# script called PauseController. This will be a very simple script; it’s 20 lines including whitespace, because all it needs to do is update the isPaused parameter of our animator controller. Here it is, in all its glory:

You’ll want to add this to the scene, so go ahead and drop it on the Canvas game object and drag the animator component to the canvasAnimator slot in the inspector.

There are a couple interesting things to note here. First, the isPausedHash bit. Animator parameters are identified by strings in the animator window and when getting or setting parameters in code, but this can be inefficient when updating them frequently (say, every frame). The preferred solution is to precompute the internal identifier that the animator uses to refer to the parameter, which allows for optimized lookup. Animator.StringToHash allows us to do this: is takes the string parameter name, and returns an integer identifier that we can use in its place. The only problem is that the identifier is not stable (we can’t just fetch it once then store it, because it might change between builds or potentially between plays of the game), so we simply cache it during the startup lifecycle of the component.

Input settings
Input settings
The second bit is on line 15, where we use Input.GetButtonDown("Cancel"). We could have bound the pause button to a specific key on the keyboard, but we’ve instead chosen to bind it to what is termed as an “input axis,” in this case, the “Cancel” axis. By default, this is the escape key, but it can be redefined by you, or even by the player later, and provides much more flexibility. You can find your project’s axes (and modify them or create new ones) via the Edit > Project Settings > Input menu. Note also that the Cancel axis is the same one used by the UI event system by default, so by using it, you’re providing a more consistent experience for your players.

As I mentioned, this script is very simple: when the player activates the Cancel button (again, the escape key by default), we toggle the isPaused parameter in our animator controller. After adding the PauseController component to the scene, you can test it out in play mode. There’s one problem though: activating the pause menu doesn’t actually pause the game. The easiest solution to that is to simply adjust the time scale in the PauseController when we change the isPaused parameter, but I want to take a slightly different approach, using a state machine behaviour.

State Machine Behaviours

Unity’s StateMachineBehaviour is a class, like MonoBehaviour, which you can extent and add to one or more animator states, and which provides hooks to execute code in response to certain events, like entering or exiting the state. In our case, the behavior we want is to stop gameplay when we enter the Paused state of our animator, and resume gameplay when we leave it.

Create StateMachineBehaviour
Create StateMachineBehaviour
To create the StateMachineBehaviour, go to the Animator window, select the Paused state, and click the Add Behaviour button in the inspector, beneath the Transitions controls. Click New Script, and name it something like PauseSMB. Hit enter to confirm. Here’s what a newly-created script looks like:

You can see that there are five events that a StateMachineBehaviour handles: OnStateEnter, OnStateUpdate, OnStateExit, OnStateMove, and OnStateIK. These five states provide enormous flexibility in scripting your animations, and we’ll explore them in greater depth in another tutorial. For the purposes of our pause menu, though, we only care about two: OnStateEnter and OnStateExit. Uncomment those two, and remove the rest.

Pausing and unpausing the game is a simple matter: we simply adjust Time.timeScale to be either 0 (paused) or 1 (full-speed). There are more interesting things you can do with the time scale, but for the pause menu, we’re only interested in “full paused” and “full speed.” Change Time.timeScale to 0 in OnStateEnter, and change it to 1 in OnStateExit. Remember that we added the StateMachineBehaviour to the Paused state, so this reflects our expectations on how the game should behave: when we enter the state, the game should pause, and when we leave it, the game should resume again.

Here’s the code:

Just like the PauseController class, this is very simple. As I mentioned, we could have put this logic in the PauseController itself, but by instead putting it in a state machine behavior that is explicitly tied to the Paused state, we’re making a clean separation between “telling the game to pause” and “telling the game what to do when it’s paused.” Now it doesn’t matter if we always trigger the pause transition from the PauseController or if we eventually decide to add a new way to pause the game: the logic is in one place.

Setting Animator to Unscaled Time
Setting Animator to Unscaled Time
If you enter play mode and test things out, you’ll notice something strange: the game pauses properly (you’ll want to add something moving to the scene to confirm this), but the pause menu doesn’t appear until you unpause the game (at which point, it suddenly appears and plays the proper “transition out” animation). This is because by default, the Animator component respects Time.timeScale. When we hit escape to bring up the pause menu, we successfully transition to the Paused state, and then immediately halt the progress of the animation by setting Time.timeScale to 0 (you can confirm this by going to the Animator window and seeing the transition bar stuck at the very beginning). This is great for keeping character animations in sync with the game speed, but doesn’t work as well for animations that only happen while the game is paused. To remedy this, select the Canvas gameobject in the hierarchy, find the Animator component, and change Update Mode from Normal to Unscaled Time (remember to exit play mode first so that your changes are preserved).

At this point, we’ve set up our pause menu UI, animated it, and added code to allow the player to pause and unpause the game. We could call it good, but in the next section, we’ll talk about how to add a little polish to what we’ve created. Thanks for following along so far!

Leave a Reply