跳转至

13 Advanced Animation Principles

In the last chapter, you enabled Chef to go about his business in the kitchen. With the help of the tween library, ingredients drop into the trough now. Chef can pick them up, wash them, chop them, place them on the plate and serve them.

But Chef himself is stuck in the same pose throughout.

img

Fortunately, Chef came prepared with his own set of animations!

Open the starter project for this chapter, then open the Kitchen scene from Assets / RW / Scenes.

Take a look in Assets / RW / 3D / Chef / Animations and you’ll find a collection of animations that were imported with the 3D model. Click the arrow to expand one of them, then select the icon that looks like a moving triangle.

You’ll be able to play a preview of the animation in the Inspector window.

img

You have animations for all the following states:

  • Chop
  • Idle
  • Idle-Hold
  • Pick Up
  • Walk
  • Walk-Hold
  • Wash

In this chapter, you’ll learn how to build up an animation state system using the Animator Component, including how to transition between states, and different ways to trigger the transitions.

Animator component

In the previous chapter, you created an animator controller for the arrow scrolling animation. It triggered the scrolling animation, which remained in the same state forever. However, the animator controller is a lot more powerful than that.

If you recall, the animator for the arrows looked like this:

img

By the end of this chapter, you will have created an animator that looks like this:

img

First things first. You need to create the new animator controller. Navigate to Assets / RW / Animations in the Project view and select AssetsCreateAnimator Controller from the main menu bar. Name it Chef_Animator.

img

Then, in the Hierarchy view, assign this new animator controller to the Animator component on Chef under Player / Chef.

img

Save the scene. Chef is now ready to be animated.

Importing the animations as states

The animator controller is currently empty. Well, it has three default states: Entry, Exit and Any State (depending on your editor layout, you might have to zoom out a bit to see all three). These states alone don’t provide any animations. So the first task is to create some states that Chef does need.

Double-click the Chef_Animator to open it in the Animator window. Then right-click inside the grid area to bring up the context menu and select Create StateEmpty.

img

The new state will be created and will automatically become the default state. It highlights in orange to show this, with an arrow coming from the Entry state to the new one. Rename the new state Chef_Idle and assign the Chef_Idle clip to the Motion parameter.

img

This is already enough to animate Chef! Click Play and you can see that Chef now bobs up and down a little while he waits on you as you set up the rest of the animator.

There are a lot of animations to set up, so it’s time for a shortcut. You may have recognized the icons in the last step as those you took a peek at in the beginning of this chapter. That little dashing triangle is the symbol for an Animation Clip, and rather than creating new states and then assigning clips, you can bring clips directly into the Animator window.

Navigate to the Project view and use the filter button to filter for Animation Clip.

img

Now select the following clips and drag them into the Animator window:

  • Chef_Idle-Hold
  • Chef_PickUp
  • Chef_Walk
  • Chef_Walk-Hold

img

This creates new states — already named and with the animation clips already assigned.

Space out the new states so that you can see them better. Make a X shape with them, with Chef_Walk to the right of Chef_Idle and Chef_Idle-Hold and Chef_Walk-Hold above them. Put Chef_PickUp in the middle of them all.

img

Now you have the beginning of an animation system. It’s time to get Chef moving!

Animator transitions

Notice there’s already a transition arrow from the Entry state to the Chef_Idle state. In order for the state to change throughout the animator controller, you need to set up transitions in each possible direction so that one state could go to the next.

In order to make a new transition, all you have to do is right-click the state you want to transition from, then select Make Transition from the context menu that appears.

img

You’ll then have an arrow line attached to your mouse, which will snap to any state you hover over. Left-click the state you want to transition to.

img

To start with, right-click Chef_Idle and select Make Transition, then hover over Chef_Walkand left-click it to select the transition destination.

Once you’ve created the transition, left-click on the arrow to bring up a preview of the animation in the Inspector.

You’ll see all the settings associated with the transition, as well as a preview of how the two animations blend together.

img

Take a look at what each of the settings does:

  • Has Exit Time is used when you automatically want a state to end and move to the next state.
  • Exit Time is the time the previous state must have played before the next state is evaluated. For example, you may want a player to finish their jump animation before they can move into an attack animation.
  • Fixed Duration If this is checked, then Transition Duration is evaluated as seconds. If it’s unchecked, then the transition is evaluated as a fraction (0-1) of the animation.
  • Transition Duration is controlled by Fixed Duration. It’s represented on the timeline as the area between the two blue markers.

img

  • Transition Offset is the offset of the animation that the next state should start at. For example, setting this to 0.5 would make the animation start half way through its clip.

img

  • Interruption Source comes into play on the occasion that more than one state could possibly transition to the next. Using the interruption source, you can dictate the priority of animation states. However, states that transition from Any State will always be added first in the queue.

With all that in mind, it’s time to build some transitions! Get ready, because there are a few steps to complete here. The first step is to create each transition you need.

  1. You should already have a transition from Chef_Idle to Chef_Walk from earlier.
  2. Add a transition in the other direction — from Chef_Walk back to Chef_Idle.
  3. Repeat these steps with the Hold states, with transitions going back and forth between Chef_Idle-Hold and Chef_Walk-Hold.
  4. When Chef puts something down, he needs to return to the Idle/Walk state. Add a transition from Chef_Idle-Hold back to Chef_Idle.
  5. And another from Chef_Walk-Hold to Chef_Walk.
  6. However, when Chef picks up an ingredient or plate, you have a specific animation to play. To get from the non-holding to the holding states, you want to pass through Chef_PickUp to trigger that animation first.
    • Add a transition from Chef_Idle to Chef_PickUp.
    • Add another from Chef_Walk to Chef_PickUp.
    • Then create a transition from Chef_PickUp to Chef_Idle-Hold.
    • And finally, add another from Chef_PickUp to Chef_Walk-Hold.

Once you’ve completed all these steps, your animation state map will look like this:

img

Now it’s time to edit some of the settings for these transitions. Each of these state changes, with the exception of the two coming out of Chef_PickUp, are going to be triggered by user input, and you want them to happen instantly. Go through each of the transitions and uncheck the Has Exit Time field.

Finally, you want the Chef_PickUp state to happen the second the user presses the space bar. As such, you want any previous state to be interrupted by the Chef_PickUp state. Change the following transitions to have Interruption Source set to Next State.

  1. Chef_Idle-Hold to Chef_Idle
  2. Chef_Walk-Hold to Chef_Walk
  3. Chef_Idle to Chef_Walk
  4. Chef_Walk back to Chef_Idle

img

This way, no matter what state the player is in, when they press Space to pick up something, the Chef_PickUp animation will play instantly. These decisions on how to prepare the settings of an animator controller are key to making your gameplay as responsive as possible.

Speaking of responsive, you have so far set up the how of animation transitions. But before it can be tested, you need to set up the when and why.

Animator parameters

In order to change the states in your animation, you need a way for the user input that’s already captured in code to be passed to the animator controller. As you know from the previous chapter, there’s already code in here to make Chef walk around and pick up ingredients. So all you need to do is add a connection from those pieces of code to the animator controller.

The first thing you’ll need is a way to transition between the walking and idle states. In the Animator window, click the Parameters tab, and then click the + button.

img

As you can see, there are a few options available here. The first three are value-related parameters: float, int and bool. These can be set from code, so long as there is a reference to the animator component. You’ll get to the last trigger in the next section.

Float Condition

Add a float parameter and call it Speed. Then, select the transition between Chef_Idle and Chef_Walk. In the Inspector, find the Conditions list and add a new condition. The new Speedparameter will be selected by default. Set the value to Greater than 0.1.

img

Now, select the reverse transition, and add the condition again — but this time set the value to Less than 0.1. And while you’re here, do the same with the transitions between Chef_Idle-Hold and Chef_Walk-Hold.

  1. Chef_Idle-Hold to Chef_Walk-Hold should trigger when Speed is Greater than 0.1.
  2. Chef_Walk-Hold to Chef_Idle-Hold should trigger when Speed is Less than 0.1.

Now that the animator controller is set up to receive a value and trigger transitions, all that’s left is to have the code send that information to the animator controller.

In the Hierarchy, select the Player object and take a look at the PlayerController component.

img

You can see at the top of this component are two values of interest.

  1. Player Speed: A float value.
  2. Move Input: An InputAction that defines the controls to move Chef.

Open the PlayerController script, and navigate down to the FixedUpdate method:

private void FixedUpdate()
{
    if (moveInput.enabled)
    {
        Vector3 move = 
            new Vector3(-moveInput.ReadValue<Vector2>().y, 
                        0,
                        moveInput.ReadValue<Vector2>().x);
        if (move.magnitude > 0.01f)
        {
            Vector3 targetForward = 
                Vector3.RotateTowards(transform.forward, 
                                      move, 
                                      6.238f * Time.fixedDeltaTime,
                                      2);
            controller.Move(playerSpeed
                            * Time.fixedDeltaTime
                            * move);
            transform.forward = targetForward;
        }
        else
        {
            controller.Move(Vector3.zero);
        }
    }

}

This code reads the input values from the controls listed on the component that you can see in the inspector. It then translates those values to the Character Controller component, which is also on the Player GameObject. The character controller has a Move method that takes a Vector3 to control the direction in which the player should move. If there’s input, a value is passed to that Move method. And if there’s no input, it tells the character controller to stop moving.

Using this information, it requires just one line to get the animations transitioning between Idle and Walking states. At the bottom of the FixedUpdate method, add this line:

animator.SetFloat("Speed",
                  controller.velocity.magnitude / playerSpeed);

Take a look at what’s happening here:

  1. animator is a reference to the animator component.
  2. SetFloat is a method that allows you to set values for parameters in the animator controller.
  3. "Speed" is the parameter you set up earlier.
  4. controller.velocity.magnitude / playerSpeed is a way for you to calculate how much the player is moving.

OK, so there may have been a small white lie there. There’s a little extra code required, and that is to set up the animator reference.

Scroll up to the top of the PlayerController class. Underneath the declaration for the CharacterController, you need to add a new reference for an animator component.

private Animator animator;

Then, scroll down to the Start method. There, you’ll see that the CharacterControllerreference is assigned. Below that, add this line to assign the reference for the new animator.

animator = GetComponentInChildren<Animator>();

Save your script and head back to the Unity editor. Click Play and walk around in the scene. You’ll see Chef’s hat start to bob from side to side as he walks around. Select Player / Chef in the Hierarchy with the Animator window open, and you’ll be able to see the animation states transition in real time. Since you added a threshold of 0.1 for Speed, this transition will only happen when Chef moves fast enough!

img

Boolean Condition

Now that you’ve seen how to set up parameters and transition conditions, and how to change them from the game code, it’s a straightforward process to toggle Chef’s holding animations.

Head back into Animator window and make sure you have stopped the game. Add a new parameter — this time a bool named Holding.

img

It’s set to false by default, which is what you want. Now, you need to set up the conditions that are affected by this new parameter. Add conditions to the following transitions:

  1. Chef_Idle to Chef_PickUp should trigger when Holding is true.
  2. Chef_Walk to Chef_PickUp should trigger when Holding is true.
  3. Chef_Idle-Hold to Chef_Idle should trigger when Holding is false.
  4. Chef_Walk-Hold to Chef_Walk should trigger when Holding is false.

img

Note: Remember that where a transition has a condition, Has Exit Time should be unchecked in the settings.

With the conditions set up, it’s time to get back into the code to trigger the changes. Open the PlayerController script once more. As you know, Chef is already programmed to pick up ingredients, wash them, chop them, put them on a plate, pick up the plate and put it on ThePass. At the top of the PlayerController class, you’ll find a number of variables that help control all of these states. This one is of particular interest:

// state for holding something or not
[SerializeField]
private bool holding;

This bool is already set up to switch states depending on if Chef is holding a plate, an ingredient or nothing at all. In your code editor, right-click the variable holding and select Find All References. Doing so will show that the state changes at four points in the code.

img

Note: If your code editor doesn’t have this option, you can always just use Find to find where these lines are.

Click each of the references where the holding variable gets set to true/false. There should be an empty line after each left for you to add in the following statement:

animator.SetBool("Holding", holding);

As before, this line is passing a variable over to your animator controller:

  1. animator is the reference to the animator component.
  2. SetBool is a method that allows you to set values to parameters in the animator controller.
  3. "Holding" is the parameter in the animator controller that you added earlier.
  4. holding is passing the current state from code.

Note: You’ve added this four times already. If you have trouble finding them, they were in the following methods: CheckPass - inside the second if statement. TakeIngredient - before the final call to ToggleMovement();. SetIngredient - as the final line of the method. And TakePlate - again as the final line of the method.

Save the script and head back to the Unity editor. Click Play once more and go to pick up an ingredient using the space bar.

Chef’s hand will now animate to grab those ingredients ready for preparing delicious meals for the Veggie Warriors!

img

Animator triggers

The Chef animations are almost complete. But if you remember from when you first looked at them, you have two more that have yet to even be added to the animator controller. Chef needs to be able to Wash and Chop the ingredients. Unlike the walk and holding states though, these are actions that are triggered and then play out for a set duration. This is where the Trigger parameter and Has Exit Time come into play.

But first, you need to add these animations to the animator controller. Find the Chef_Chopand Chef_Wash animations using the same trick as earlier, and drag them onto the Animatorwindow. Space them out near the top of the existing graph.

img

The player will trigger these animations by pressing the Ctrl key when near the sink or chopping board. In either case, it doesn’t really matter what animation state Chef was in — you need him to transition to washing or chopping right away. This is the reason for the Any State state.

Add two new transitions from Any State to the new Chef_Chop and Chef_Wash states.

When Chef is finished washing or chopping, he takes the ingredient back into his hands. So, you need transitions going from the new states back to the holding states. Add four more transitions:

  1. From Chef_Chop to Chef_Idle-Hold.
  2. From Chef_Chop to Chef_Walk-Hold.
  3. From Chef_Wash to Chef_Idle-Hold.
  4. And from Chef_Wash to Chef_Walk-Hold.

Then final graph will look like this:

img

New transitions have Has Exit Time checked by default, so you don’t need to change any of the settings for the transitions coming out of Chef_Wash and Chef_Chop. This means the wash and chop animations will play their duration, and then return to the Chef holding animations.

But you do still need to set up how the wash and chop states are triggered. For this you need two new parameters. Add two new parameters in the animator. Both should be triggers. Name them Wash and Chop.

img

Triggers work differently than the other parameter types. They are for one-shot settings. When the trigger is set on, a transition using that trigger will take place immediately. The trigger will then automatically be off, ready to fire again. This is perfect for the actions you need to create here.

For the Any State to Chef_Chop transition, add the Chop condition. Notice that there is no other value or choice after the trigger like there was for the Bool and Float conditions you used earlier.

Similarly, for the Any State to Chef_Wash transition, add the Wash condition.

img

Now you know what’s coming next. It’s time to add some code to trigger the triggers!

This time however, you’ll do something a little different. You have two state transitions that need to be triggered from code, and each needs to be triggered from a different action. You could write some code around each of these actions in the player controller. However, the sink and the chopping board each already have a component called Workstation which exposes an OnInteract event in the inspector. You use this to trigger the animations when Chef interacts with these workstations.

Open the PlayerController script once more. You’ll still add some code there because there’s already the reference to the animator component. Add the following method to the class toward the end:

public void SetAnimationTrigger(string name)
{
    animator.SetTrigger(name);
}

This method simply takes a string name of an animation trigger, and triggers it on the animator component. As it’s a public method, it’ll be exposed to you in the editor. Save the script and head back into the Unity editor.

In the Hierarchy, select the Interactables / Sink GameObject. Then in the Inspector, within the Workstation component, click the + to add a new listener to the OnInteract object. Drag the Player GameObject into the field, then from the dropdown list, select PlayerControllerSetAnimationTrigger (string). Then, type Wash into the field.

img

Do the same with the Kitchen Table GameObject, but type Chop into the field. Remember that the strings you pass should exactly match the parameters added in the animator controller, or they won’t work as expected.

img

With everything in place, save the scene and click Play.

Take an ingredient over to that sink and get washing!

img

Congratulations! You’ve completely set up all of Chef’s animations, and he’s now ready to serve some much-needed sustenance to the Veggie Warriors about to head out into battle against the tank. The only thing left is to cook some delicious recipes, which you’ll do in the next chapter.

Thankfully, the ingredients keep dropping in for more dishes…

Challenge

Sometimes you may have an animation that would work either in forward or reverse motion. Using the Speed variable on an animation state, you can vary how that animation plays in the game. For a challenge, try adding another state for Chef_PutDown and add it into the animator controller for when the user puts an ingredient onto the plate or puts the plate onto ThePass.

Here’re a few hints:

  • Holding will become false at these points and the animation will transition to Chef_Idleor Chef_Walk.
  • Instead, make those transitions go through the new Chef_PutDown state and have it play the Chef_PickUp animation, but in reverse! Set the Speed variable to -1.
  • Then, have transitions go back to the Chef_Idle and Chef_Walk states.

Now when Chef puts something down, you’ll see more hand movement than before and the Chef_PickUp animation will play in reverse.

Key points

  • Animator controller controls different animation states.
  • Animation States hold animation clips.
  • Transitions describe how you move from one state to another.
  • Parameters can be used as conditions to transition between states.
  • Has Exit Time means that a transition will trigger once an animation has finished playing.
  • Parameters Float, Int and Bool are values that can be set from code using animator.SetFloat, animator.SetInt or animator.SetBool respectively. Conditions can compare these values to see if a transition should trigger.
  • Trigger is a special parameter that describes a condition that happens immediately when you ask it to via animator.SetTrigger.