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!

Beginner, Mecanim, Tutorials, Uncategorized, Unity3D

Unity3D Animated Pause Menu, Part 3: Setting up the Animator Controller

So far in our efforts to create an animated pause menu, we have set up the required UI elements for the menu, and created the animation clip that will drive the transition between the paused and unpaused states of the game. Now, we’re going to move ahead and set up the Animator Controller that will actually handle the user’s request to pause or unpause.

Default Pause Menu Animator In our Unity3D project, click the Canvas gameObject in the inspector, then open the Animator window (as far as I know, there is no hotkey for this, but you can find it in Window menu under the Animator label). You should see a fresh animator editor with three states: Any State, Entry, and Pause (or whatever you called the pause animation that we created in step 2), with a transition from Entry to Pause.

Creating a new default state
Creating a new default state (click to enlarge)

Any State doesn’t concern us in this tutorial, and Entry just indicates which state the animator controller will immediately transition to upon starting. This is currently Pause, but we’d like to start the game in an unpaused state, so go ahead and right click on the editing surface and select Create State > Empty, then right click on the new state (imaginatively called “New State”) and select Set as Layer Default State. In the inspector, go ahead and call this state Unpaused.

Now we need to create a transition, which is a set of conditions under which the animator controller will move from one state to another. In this case, our transition conditions are very simple: if the game is paused, we transition to the “Paused” state. To define this, we will create a new state machine parameter.

State Machine Parameters

State machine parameters are simply pieces of data that inform the animator controller about the condition of the game. They can take the form of floats, integers, booleans, or triggers (which are just a special case of boolean that resets immediately after being consumed by the state machine), and they are used in transition conditions to determine how the state machine should behave next. What makes them so powerful is that they can be set from code, which we’ll explore in the next segment of this tutorial.

In the case of the pause menu, we will drive it with a single boolean parameter: isPaused. To create this, select the Parameters tab on the left of the animator window, click the + icon, and select Bool. Name it isPaused and hit enter. You can leave it unchecked (the default).

State Machine Transitions

For our new parameter to be useful, we need to create some transitions that use it. As I mentioned above, a transition is a set of conditions that govern when the state machine moves from one state to another. These conditions can be timing-based (“wait for the end of this animation,” for example), or they can be condition-based.

Setting up the transition (click to enlarge)
Setting up the transition (click to enlarge)
We’ve already discussed one example of a condition: isPaused = true. Create this transition by right clicking on the Unpaused state, clicking Make Transition, then click on the Paused state. The transition is indicated by a white arrow pointing Unpaused -> Paused. Click on the transition arrow to select it, then in the inspector, uncheck Has Exit Time. This indicates that we don’t want to wait to transition: when the isPaused condition (which we will set up next) becomes true, the state machine should transition immediately from Unpaused -> Paused. In the Conditions list, click the + icon. Since isPaused is our only parameter, it will be selected by default, but if you had multiple, you could find it in the “key” dropdown on the left. Make sure the “value” dropdown is set to true, and we’re done setting up this transition.

You can test our progress so far by entering play mode, selecting the Canvas game object, and clicking the isPaused checkbox in the parameters list. You should see the pause menu animate in (if it’s animating in over and over again, you may have forgotten to uncheck the Loop Time box in the animator clip inspector), and if you uncheck the isPaused parameter checkbox, you should see… nothing. The pause menu will sit there, smugly defiant. This is because we’ve told the animator controller how to animate from Unpaused to Paused, but we haven’t yet specified how it should transition back to Unpaused when the player unpauses the game.

If we wanted to make the menu disappear instantly, we could just set up a transition from Paused -> Unpaused, give it a condition of isPaused = false, and be done with it. For completeness’s sake, let’s say that we want to play the same animation that we play when we’re pausing, but in reverse. To do this, we need to create a third state, which we’ll call Unpausing. Set a transition from Paused -> Unpausing (while you’re at it, you can create a transition from Unpausing -> Unpaused, which we’ll set up shortly), remembering to uncheck the Has Exit Time checkbox, then give it a condition of isPaused is false. Click on the Unpausing state node, and drag our pause animation into the Motion property slot of the inspector. This will indicate that we want to use the same animation. To specify that we want that animation reversed, change the Speed property from 1 to -1. Specifying a negative speed essentially means that we start at the end of the animation clip, and work our way forward, effectively performing all the motion encoded in the animation backward.

The last thing we have to do is to configure the transition Unpausing -> Unpaused. Remember how we discussed that one of the types of transitions is based on timing? That’s exactly what we want. The Unpausing state starts playing our transition animation. As soon as that’s done, we want to leave the Unpausing state and transition back to the Unpaused state. Click on the transition arrow pointing from Unpausing to Unpaused, and open the Settings flyout. Set Exit Time to 1 and Transition Duration to 0. Leave the Conditions list empty.

Go ahead and test it out in play mode, checking and unchecking the isPaused parameter to see the menu slide in and out smoothly. One thing I notice, though, that the exit animation (which is again just our enter animation played backward) seems to take a little too long: when I’m leaving the pause menu, I want to get back into the action quickly! Click the Unpausing state and change the speed multiplier from -1 to -2. Not only does this indicate that we want to play the animation in reverse of how it was recorded, we also want to play it at double speed. You can drive this value with a parameter, which opens up all sorts of possibilities, but that will be a subject for another lesson.

That’s it! You’re done setting up the animator controller! In the next installment, we’ll talk about how to drive the animator from code so that we can actually pause and unpause the game. Stay tuned!

Beginner, Tutorials, Uncategorized, Unity3D

Unity3D Animated Pause Menu, Part 2: Animating the UI elements

Following the setup of all the UI elements for the pause menu in part one, we’re now going to create an animation for the transition between the unpaused and paused states of the game. As a reminder, here’s a preview of the finished state of the pause menu, showing the transition animation:

Pause menu animating in and out with example gameplay in background
Pause Menu in Action

Before we get started, let’s talk a little bit about animation in Unity3D. Unity’s animation system, Mecanim, uses curves to represent motion. Most commonly, these motion curves drive the movement of 3D models, but you can actually use Mecanim to animate any public property that Unity knows how to serialize. We’re going to use this to our advantage in our pause transition to move UI elements around the screen.

With Mecanim, you can either edit the curves directly, or use the keyframe-based dopesheet to specific values, and let Mecanim figure out the curves for you. In this case, we’ll be use the dopesheet as it’s easier to work with and we’re not going to be doing anything too complex.

Open the Animation window (⌘6 on OSX, ctrl-6 on Windows), select the Canvas game object in the hierarchy, and click the Create button. Doing this will prompt you to create a new Animation asset, as well as creating a new animator controller and adding an Animator to the Canvas game object if it doesn’t already have one. Note that you could technically animate the menu elements directly, but as we split them up to allow for more complex transitions, and because we want to later be able to animate multiple, discrete UI elements with one animator controller, we’re going to add the animator to the Canvas, which is the highest-level parent of a UI element.

After creating a new animation clip, we enter “record” mode by default, but first we want to set all our pause menu UI elements to their default (unpaused) state. Click the red Record circle exit record mode, then select the panel background, panel contents, and title text, and move them off-screen. To achieve the transition effect I wanted, I moved the panel background on the x axis to the right, the panel content on the x axis to the left, and the title text on the y axis up. They don’t have to be a huge distance off-screen, just enough so that they’re not visible during gameplay (you can use the Game window to check this, or enter play mode to try it out). My UI view now looks like this:

Current UI Setup
Current UI Setup

With our initial state set up, we’re ready to animate! Select the Canvas game object and pick your pause animation out of the dropdown menu if it’s not already selected, then hit the red button to enter Record mode. You know that it’s working properly when your editor play mode buttons turn red:

We’ll want to add three properties: Panel Background > Rect Transform > Anchored Position, Panel Content > Rect Transform > Anchored Position, and Title > Rect Transform > Anchored Position. This adds keyframes for each property at 0:00 and 1:00 (one second). If you played your animation now, it would do… a whole lot of nothing, because the beginning and ending keyframes have the same value, which is in effect saying “do nothing for one second”. Let’s change that. Drag the playback head (the red line) to the one second mark (clicking on the keyframe at the time you want to change won’t have the desired effect) and “zero out” the Anchored Position properties, setting each UI element in the pause menu to the position that it should be in when the game is paused. You can either set them in the Animation window directly, set the appropriate fields on the Rect Transform in the inspector (animated fields turn red as well when in record mode), or reposition the elements by hand in the scene view.

With that done, hit play in the animation window. You’ll see the different elements of your pause menu slide in, but one second to bring up a menu is almost painfully slow. Players are nothing if not impatient, so we need to make the transition snappy to avoid frustrating them. Plus, I think that, rather than all the motion happening at once, it will look better if the background flies into place first, followed by the text and buttons sliding in, followed by the logo dropping into place. Exit animation play mode, and adjust the animation by dragging the keyframes in the dopesheet. I found that about a quarter second per element (so, multiples of 0:15 on the timeline), with the next element starting its motion as the previous reached its destination, felt about right for the feel I wanted to achieve. Here’s a look at my finished dopesheet, about 50% through the full animation (as indicated by the red playback head line):

Completed Dopesheet

With that, we’ve completed the animation for our pause menu elements. The last thing to do is select the animation clip asset we’ve created and uncheck the Loop Time property in the inspector, as a preparation for creating the pause transition state machine in the next tutorial post. Thanks for following along!

Beginner, Tutorials, Uncategorized, Unity3D

Unity3D Animated Pause Menu, Part 1: Setting up the UI

Today, we’re going to start building an animated pause menu in Unity3D. We’ll go through setting up the UI, animating the UI elements, creating the animator controller / state machine to transition from unpaused to paused, and actually pausing the game in response to player input. We’ll even get into a bit of polish and shine by making the game visible but blurred while the game is paused.

Here’s a preview of the finished product:

Pause menu animating in and out with example gameplay in background
Pause Menu in Action (click for higher quality)

Setting up the UI

Let’s get started! First things first, we’ll want to add a UI canvas (Create > UI > Canvas) if the scene doesn’t already have one. Since part of the pause animation involves the various components swooping in from off-screen, we want to make sure that our pause menu elements never accidentally appear on screen until we want them to.

Canvas Scaler Component With that in mind, go ahead and add a Canvas Scaler component to the Canvas GameObject. This controls how the UI elements parented under the canvas are scaled at various resolutions. I like to use Scale with Screen Size with a Reference Resolution of 1024×768, but you can set the reference resolution to whatever works for you. Either way, set the Match property to 0.5 to take screen width and screen height into account equally. This should help avoid situations where the edges of your pause menu UI elements become visible at higher resolutions.

Now we need to create the menu itself. The transition I have in mind is for the background panel of the menu to come in from one side, the buttons and text of the menu to come in from the other, and the game name or logo to drop in from above. This means that, while I would normally add the buttons as children of the background panel, I’m going to need to add the buttons to a sibling object of the panel. This will allow me to animate them separately without conflicts.

Anchor Presets
Anchor Presets
After adding the background panel (Create > UI > Panel), you’ll want to open the anchor presets by clicking on the crosshair-looking box in the upper left of the panel’s Rect Transform and shift-click the middle button to set the anchor and pivot of the panel to the middle of the screen. Then you can change the width and height of the panel. I chose 300 x 330 for a nice, chunky-looking box.

Now to create the content (buttons and text) that will go into the box. Select the canvas and add an empty child (Create > Create Empty), name it something like “Panel Contents,” then repeat the steps that you followed for the background panel: set the anchor and pivot to the middle of the screen, and give it the same height and width as the background panel. I added some text that reads “Paused” and set the anchor/pivot to stretch across the top of the container, then added another empty child to the Panel Contents game object, called “Buttons.”

On the Buttons game object, add a Vertical Layout Group component, which will automatically position our menu buttons in a nice column. Set Child Alignment to “Upper Center”, and make sure the width and height buttons are both checked for Child Force Expand, ensuring that the buttons use all the available space inside the layout group. Set padding and spacing values to whatever looks good to you: padding is how much space will be between the edge of the menu box and the buttons, and spacing controls how much vertical space there is between buttons. If you’re not sure, start with 10 and adjust from there. Make sure the anchor and pivot are set to the center of the container for the “Buttons” object, and give it a little space on Top in the Rect Transform so that the buttons don’t overlap with the “Paused” text.

The last step is adding a logo or other game text above the menu, which is just a text UI object with its anchor set to the top middle of the screen. If you’re using text and it vanishes when you try to make it larger, either increase the width and height of the text’s Rect Transform, or set Horizontal and Vertical overflows to “Overflow” in the text component’s Paragraph section.

The Situation so Far
The situation so far

In the next post, we’ll work on the animation that we’ll use for the pause transition.

Implementation Notes

The font I used was Bungee Regular, which is available under the SIL Open Font License.

If you need help with a color palette, searching “material design color palette” online can give some great examples to get you started. In particular, I like to use the colors provided by clrs.cc while prototyping.

Cookbook, Uncategorized, Unity3D

Unity3D Cookbook: Lerp

Let’s take a look at some common problems and anti-patterns when using lerp in Unity, and how to correct them.

Anti-pattern #1: lerping with Time.time

We want to move a GameObject to a target point over time, so we lerp between a starting point and a destination, using Time.time as the t value.

The Problem

As discussed previously, the lerp function produces a value that is some percentage of the distance between two known points, where that percentage is represented by a number in the range of 0 to 1 inclusive. Time.time represents the number of seconds since the start of the game, and quickly becomes greater than 1!

The Solution

Cache the time that the operation started, and use that and the desired duration of movement to calculate how far along you are.

Anti-pattern #2: lerping in place

This is a similar problem to #1, but can be even more subtle. We want to change some member property in our script from its current value to a target value, so we update the property in place. This is commonly coupled with using Time.deltaTime as the t value (see the next section).

The Problem

Unpredictable movement along the interpolated points; reaching the target position too quickly.

The Solution

Remember that linear interpolation operates predictably between known, fixed points. Calling lerp will always produce the same value when the same endpoints and t value are used. When you update one of the values in place, you’re effectively “sliding” one of the endpoints, which means that calling lerp with the same t value will produce a slightly different result. Done in a 60 fps loop, these errors can add up to produce noticeable effects on gameplay. The solution is, again, caching: in addition to caching when you started lerping, also store the initial value of whatever you’re lerping.

Anti-pattern #3: lerping with Time.deltaTime

Used along with lerping in place, we again want to move a given value toward a target over time, so we pass Time.deltaTime as our t value.

The Problem

This usage of lerp can produce questions about behavior such as “my object never reaches its destination,” or “my game object slows down as it nears its destination.” The accumulation of rounding errors can eventually cause the object to “cross the gap” and reach its destination, but there is no guarantee of that, especially if we are relying on exact comparisons (transform.position.Equals(targetPosition)) to determine if we should execute further game logic.

The Solution

Time.deltaTime produces small values representing the number of seconds it took the last game frame to run. When the game is running at 60 fps, this number is very small: typically around 1 / 60, or 0.016667 seconds. When passed to lerp as something like lerp(a, b, Time.deltaTime), we are essentially saying “give me the value that is ~1.6% of the distance between a and b“. If we weren’t updating our lerped value in place, we would see the results jittering ever so slightly near the starting point. The only reason it works at all is, again, because we are “sliding” the starting point gradually toward the destination point.

The solution is the same as previous: instead of using Time.deltaTime and updating in place, cache the starting value and timestamp, and calculate progress based on those. If, for whatever reason, you must use Time.deltaTime, you can accumulate it into a variable to accomplish nearly the same effect:

Theory, Uncategorized

Lerp: Understanding Linear Interpolation

If you’ve ever wanted to change a player’s health bar from green to red as they take damage, figure out the damage of an attack based on the power level, or smoothly transition from darkness to light in your level design, chances are you’ve used a linear interpolation, or “lerp”, operation. Having linear interpolation on your toolbelt and being confident in its application can be invaluable in game programming, but I’ve noticed that there can be a few pitfalls to be aware of as well, so let’s talk about what, exactly, it means to “lerp” something, and how we can apply it in our code.

The idea behind linear interpolation is fairly straightforward: it is finding an unknown value that lies some percentage of the distance between two known values (technically, it can be an arbitrarily large set of known values, but in game dev, I’ve found that it’s most often two). For the sake of clarity and consistency, let’s call the first known value “a”, the second known value “b”, and the percentage of distance “t”. In function form, we would write it as something like this: lerp(a, b, t).

Here to demonstrate this is a little visualization tool that I built. You can drag the points around to experiment with linear interpolation between two points (the green point represents the interpolated value); hovering or touching a point or colored part of the equation at the top will bold all the matching values:

See the Pen Linear Interpolation (lerp) by Derek Stobbe (@Problematic) on CodePen.

A couple things to note. First, the arguments are all either numbers, or things composed of numbers. This means, naturally, that you can lerp between floats and integers, but you can also lerp between things like colors, numeric vectors (like the Vector2 and Vector3 types in Unity), and custom data types composed of numbers, by lerping each component value individually (Unity provides wrappers to do this for the first two via the static methods Vector3.Lerp and Color.Lerp).

Second, notice an interesting property of interpolation: when the (blue) t value is 0, the green interpolated point is equal to the first (red) point. When t = 1, the interpolated point is equal to the second (purple) point. Based on that, you can surmise that when t = 0.5, the interpolated point is halfway between the two, t = 0.75 is three-quarters of the distance from a to b, and so on.

Note that the visualization tool allows you to explore values of t that are less than 0 or greater than 1, but at that point, you’re no longer interpolating a value, you’re extrapolating one. Many implementations of the lerp function (including Unity’s Mathf.Lerp) clamp the t value between 0 and 1, meaning you won’t be able to reproduce the results above where 0 > t > 1: when clamping input, lerp(a, b, 1.12358), for example, gives the same result as lerp(a, b, 1.0).

With this in mind, let’s talk about one of the places I frequently see developers get tripped up: using lerp to interpolate between values, where the t value is driven by time. Consider this pseudocode:

In this example, while the player holds a button, we’d like some value representing the player’s attack power to be lerped from an initial value of 0 to target_value over a period of time, say one second. However, there’s a bug in this implementation. Remember how the t value of the lerp function is clamped to the range [0, 1]? Assuming our elapsed_game_time value (which would be represented by Time.time in Unity, for example) is a float that represents the number of seconds the game has been running, and begins incrementing from 0 the moment the game starts, this code will only work as expected in the first second of gameplay. After that, the “elapsed time” will be some number greater than one, and the call to charge_attack will immediately set attack_power to the target value, instead of ramping it up over time, letting the player make fully-charged attacks with impunity, thus destroying our perfectly-tuned game balance.

How can we address this problem? We need to remap our t value to be between 0 and 1 inclusive, and the easiest way to do that is to cache the time that the user started pressing the button. In our example implementation, that might look like this:

The only thing we do differently is to cache the time that the button was pressed, and subtract that from the currently elapsed game time when we call charge_attack to update attack_power during the game loop. We can see that this works for our desired duration of one second, because say that the button is pressed at 3.1 seconds of elapsed gameplay, for example. After a half second, so at 3.6 seconds on the game clock, our calculation for the t value is 3.6 - 3.1, which is 0.5… precisely the value we wanted.

What if instead of changing attack_power to target_value over one second, we want to lerp it over a longer period of time, say 2.5 seconds? Our implementation fails once more, because one second after the button is pressed, elapsed_game_time - button_pressed_at exceeds 1.0, and is clamped. How can we solve this?

Well, if we look at our current solution, we can see that elapsed_game_time - button_pressed_at gives us a fractional number representing our current progress toward “filling” attack_power by making it equal to target_value. We know that any number divided by 1 is equal to the number itself, so we can represent our progress with the formula (elapsed_game_time - button_pressed_at) / 1.0. Coincidentally (or not), 1.0 happens to represent the old requirements for the duration of the charge effect, so if we swap out that constant for a variable representing the charge duration, we’re back in business:

Everything works as expected, and we have the added benefit of being able to easily tune charge_duration to control how long it takes the player’s attack to lerp up to full power. Game balance remains intact!

As I’ve hopefully demonstrated, the problem domain for successfully using linear interpolation in your games mostly involves figuring out how to map your t progress variable to a floating point number between 0 and 1. I’ll talk about Unity-specific implementations in another post, but I hope that’s enough to get you going for now!

uGUI, Uncategorized, Unity3D

Animating Multiple UI Elements with One Animator Controller

Have you ever wanted to animate multiple UI elements at once in Unity? Unity3D 4.6 brought with it the long-awaited Unity GUI (or uGUI) system, and with that, a lot of potential for creating great in-game UIs. When building your game’s UI, you will probably want to animate different aspects of it, whether that’s sliding a menu in from off-screen, fading in text, or any of a number of other effects. One option is to create separate animator controllers for every element, but that can quickly get out of hand: the number of assets to keep track of increases for every UI element you want to animate, not to mention the possible performance implications of running many separate animators at the same time.

A better solution is clearly to use a single animator controller to manage animations for multiple parts of your in-game UI, but there are some caveats. Animating an element implies having multiple states per element: fading in and out “Game Paused” text, sliding a panel on and off screen, and so on. Trying to manage all of these with a single state machine will result in a combinatorial explosion of required states… GamePausedPanelShown, GameUnpausingPanelHidden, GameUnpausedPanelShown, GameUnpausedPanelHidden are an incomplete list of states that you might require for just two animated elements!

The answer is in animation layers. You might be familiar with layers as allowing you to do facial animation in addition to action animations for your 3D models, but we can apply them here by creating one layer for each element you want to animate and change at runtime.

Animating UI Components with Layers
Animating UI Components with Layers

In this example, I’m demonstrating the scenario I mentioned before: I want to fade in and out a “Pause” text in response to the user pausing the game, and I also want to be able to slide a panel on-screen to display some non-critical information to the player upon request.

For the pause text, I have a simple 3-state state machine, and transition between them by setting a boolean isPaused animator parameter in response to user input. I’m doing something similar to animate the panel, with an isPanelVisible parameter. The Base layer is empty. All told, it took me five or ten minutes to set up.

However, if you repeat my setup and try to pause the game, you may be dismayed to find that your UI isn’t animating at all. That’s because there’s one additional step, the “secret sauce” to making this work: setting the layer weights. Unity defaults the weight of a new layer to 0, meaning it won’t contribute to the overall animation at all. You’ll want to set the weight of all your UI animator layers to 1, meaning the animations played by the layers’ states fully contribute to the final animation. This setting is found in the cog menu of the layer in the Animator window, as demonstrated in the gif below:

Setting Layer Weight
Setting Layer Weight

For a 3D model, you would tune layer weights based on how much that layer should reasonably contribute to the model’s animation, and is in general a more complex process, but for UI animation, each layer is basically independent of the others, and you want to make sure that all layers get to run their full animation. Setting all layer weights to 1 enables this.

One final thing to note is that if you have already begun animating a UI element on the Base Layer of your animator controller, and would like to move it to a new layer, leaving the default layer uncluttered, you can accomplish this by adding a new empty layer, then dragging that empty layer to the top of the layer list. Layers cannot be copied (so far as I know), but Unity treats the top layer as the default layer, giving it a forced layer weight of 1, and letting you set up multiple layers to control discrete UI components.

You can download the example Unity project.