跳转至

12 Basic Animation Principles

Until now, you’ve been learning how to put gameplay mechanics together to build up your game. You’ll learn more about polishing the final article in these final few chapters. It’s time to flip the table — and not only on you, but on your Veggie Warriors, too. :]

In this chapter, you’ll learn the basics of animation in Unity. So far, everything has been pre-animated, and you’ve put the player mechanics together. Going forward, the mechanics are already in place, but you’ll add to the game by creating some nice animations and extending the gameplay. For this, you’ll look at a new game environment - the kitchen! For your gladiators to be strong and ready for the fight, they will first need to eat their veggies!

Introduction to Unity animation types

Just like there are many ways to peel a vegetable — whether it’s a trusty peeler, a new-fangled machine or the edge of a blunt knife — there are many ways you can animate objects in Unity.

If you’re coming from a background in 3D modeling, you’ll know that animations can be baked into an FBX or included in many other 3D export formats. Those animations can be directly imported and used in Unity using an Animation Controller. You’ll get to that later. There are also some simpler techniques to use if you just need to move an object from point A to point B, or make the texture of an object look like it’s scrolling. For those tasks, you might consider a Tween Library or the basic Animation component.

Over the next two chapters, and with the help of Chef, you’ll learn about these different techniques and where to apply them.

Introducing the kitchen

You might be wondering what happened to all those Veggie Warriors that fell in battle against the tank. Well, you’re about to find out. Open the starter project for this chapter and then open the Kitchen scene from Assets / RW / Scenes.

img

It doesn’t look like much just now, but before long you’ll have this place sending meals out to help prepare those warriors for battle.

As for the ingredients, click Play in the editor and you may see a few familiar faces pop up in the scene.

Note: The functionality for this game is already set up. The objective is for Chef to take the ingredients, wash them, chop them and place them on a plate, before picking up the plate and sending it to the “pass” - the table at the bottom of the screen with the arrows on. Even though nothing animates at the moment, you can already do all this with the following controls: W, A, S, D, - Move Chef around. Space - Pick up ingredient / plate or place ingredient on plate. Ctrl - Perform Wash / Chop action.

img

Fortunately for Chef, that tank is pushing all the defeated warriors down the grate in the center of the arena, and they’re falling straight into his kitchen. A never-ending supply!

Note: That supply is actually an Object Pool — similar to the one you learned about in the previous chapter.

At the moment, the vegetables — or Ingredients, as Chef likes to call them — are just appearing up in the air. Your first task is to get them to fall into the troughs below, ready for Chef to collect.

In other words, you need to get the Ingredients from point A to point B.

img

Deciding on how to animate your GameObjects

There are many ways to animate GameObjects inside of Unity. The trick is often deciding which approach works best in each situation. You have to consider some of the following facts:

  • Does the animation need to be complex?
  • Does the animation need to transition between various states?
  • Is the animation a looping movement?
  • Is the animation always between the same values?

Answering these kinds of questions will help you decide what method to use. If the answer for either of the first two questions is yes, odds are you’re dealing with some sort of character animation. These would likely be imported with your 3D models, and then built up using an Animation Controller. More on that in the next chapter.

For a looping animation, such as a spinning coin or perhaps a scrolling texture, you would want to consider the Animation component. More on that later in this chapter.

The Animation component would also work if your animation was always between two fixed values. As you may have spotted from the images above, the Ingredients are all appearing in the same locations, but need to end up in slightly different positions (and rotations) inside the trough.

This is your from A to B process. You know where the GameObjects start, and you know where they need to end, but you need to be able to describe what happens in between. This process is often referred to as tweening.

Building a tween library

Tweening is a very common method of creating animations in Unity. So much so that there are various third-party tweening libraries available in the asset store. Some of the most popular include DOTween and LeanTween. Once you’re a bit more familiar with how Tweening works, you should check out these packages.

However, for what you need to achieve in the Kitchen scene, you don’t need a full tweening package. You’ll need just one method call and your scene will come alive.

Filling the trough

In the Hierarchy, find one of the Troughs under the Interactables parent GameObject. Select it and take a look at the PickupArea component. You’ll see three key things have been set up:

img

  1. The type of Ingredient that falls into this trough.
  2. A List of SpawnPositions as Transforms.
  3. The DropPosition, also stored as a Transform.

The DropPosition is where the Ingredients currently appear when they spawn into the scene. The SpawnPositions are where you actually want them to be. So let’s get them from A to B.

Open the PickupArea script by double-clicking on the script component or finding it in Assets / RW / Scripts. Navigate down to the Spawn method.

private void Spawn()
{
    // 1
    if (availableIngredients.Count < spawnPositions.Count)
    {
        // space for more ingredients
        // 2
        IngredientObject newIngredient
            = IngredientPool.Instance.Fetch(ingredientType);
        if (newIngredient != null)
        {
            // 3
            availableIngredients.Add(newIngredient);
            // 4
            newIngredient.transform.parent = transform;
            // 5
            newIngredient.Lerp(
                dropPosition,
                spawnPositions[availableIngredients
                                   .IndexOf(newIngredient)],
                0.5f);
        }
    }
    // 6
    timeSinceSpawn = 0;
}

There’s a lot going on in here:

  1. First, check to see if the number of available ingredients is less than the trough can hold.
  2. If so, try to fetch another ingredient from the IngredientPool.
  3. If you got an ingredient, add it to the available ingredients.
  4. Then, parent it to the trough. This means changing the location of the GameObject in the Hierarchy programmatically. Take your new Ingredient and make it a child of the trough. That way, you can deal with its position and rotation relative to the trough. In Unity, transforms have a localPosition and localRotation for this purpose.
  5. After that, call the Lerp method passing the dropPosition and next available spawnPosition.
  6. Finally, in all cases, reset a timer that called the Spawn method.

You won’t need to edit any of this code, but the one line that’s worth noting is that last one inside the second if statement:

newIngredient.Lerp(
    dropPosition,
    spawnPositions[availableIngredients.IndexOf(newIngredient)],
    0.5f);

Lerp logic

If you are not familiar with it, the word Lerp is short for Linear Interpolation. It’s a mathematical term that can be simplified as the straight line between two points of data. Using this line, you can work out any position between these two points.

In Unity you can use Lerp to help animate any number of things. You can say, “given the values a, b and the interval t, you can find any value t distance between a and b.”

img

And in Unity, a and b could be just about any value. Alpha, Color, Position, Rotation, Audio Volume — the possibilities are nearly endless, so long as you’re willing to work out the math! Math doesn’t always need to be hard, though. Consider a and b just to be simple float values. Say, 1 and 3. If you wanted to Lerp between a(1) and b(3) by 0.5, you’d get the answer 2.

  • 1 + (3-1) * 0.5
  • 1 + (2) * 0.5
  • 1 + 1

Or, you can also consider it on a graph:

img

But the Lerp method is already there, you say. Technically, yes it is. But take a closer look. Navigate to that Lerp function. Hover over the text in Visual Studio (or the editor of your choice), hold down Control or Command and Left-Click. This will take you to where this method is defined. Or, if your editor isn’t smart enough, you can open the Tween script from inside the Assets / RW / Scripts folder.

You’ll end up at this method:

public static void Lerp(this MonoBehaviour m,
                        Transform from,
                        Transform to,
                        float time)
{
    m.transform.position = from.position;
    m.transform.rotation = from.rotation;

    m.StartCoroutine(Lerp(m.transform, to, time));
}

The signature of the method should look familiar. You’re asking to Lerp something from one Transform to another over time. But if you’re observant, you’ll notice this isn’t the only Lerpmethod in the class, and you can see that this method calls another Lerp method from within.

Look at the method signatures — the variables inside the parenthesis — and you’ll see each one is different. This technique is called Overloading and is useful when you want different ways to carry out the same action.

It’s a great way to keep your code DRY:

  • Don’t
  • Repeat
  • Yourself

The first Lerp in Tween is taking just the from and to variables, combining them with a fixed duration of one second, and calling the second Lerp method. The second Lerp method adjusts the position and rotation of the object you want to animate, to the same as from, and then calls the final Lerp method via a Coroutine.

Creating the Lerp function

Note: You’re going to use Coroutines and IEnumerators again here. If you want a quick refresher on them, please review the previous chapter.

As mentioned above, the third Lerp method in the Tween script is an IEnumerator, and this is where you will actually get your objects to move. Here’s the code at present:

private static IEnumerator Lerp(Transform transform,
                                Transform target,
                                float time)
{
    yield return null;
}

The method takes three pieces of information:

  1. A reference to the Transform you want to move.
  2. Another reference Transform of where you want to move to.
  3. An amount of time that you want the transition to last.

You’re going to harness the power of the Coroutine to run the Lerp method over time, so the first thing you need to do is keep track of the time. Add the following to the method:

float elapsedTime = 0;

Next, add this:

Vector3 startPos = transform.position;

This saves the position that the object is initially starting from.

You now need to wrap all the code that will change values over time in a loop. Wrap the existing yield return null; statement in a while statement, like this:

while (elapsedTime < time)
{
    yield return null;
}

The while loop will execute while the elapsed time is less than the transition time. Putting the yield instruction inside the loop allows the rest of the game to continue running, and tells the system to come back to the coroutine in the next update.

At the moment, the while loop won’t actually ever stop looping because you haven’t updated elapsedTime. So to do that, add the following before the yield instruction:

elapsedTime += Time.deltaTime;

Time.deltaTime is a really useful Unity variable. It gives the amount of time that has passed between the previous frame and the current one. This allows you to make smooth calculations over time. By incrementing elapsedTime with this delta, elapsedTime will eventually reach the value of time, so the loop will exit.

Now, in order to actually move the object, you might have been worrying about some complex 3D vector math. Well, fear not! Unity has some nice built-in utility functions for you to use. You’ll never guess the name of the one you’re going to use. :]

Add this line inside the loop, above where you increment elapsedTime:

transform.position = Vector3.Lerp(startPos, target.position, time);

img

Yes, yes. But would you rather have to write out the vector math?

If you hover over the Vector3.Lerp call, you’ll see that it returns the value from a familiar equation.

img

However, if you run this as it is now, it still won’t work how you want it to. And that’s because you’re using the time value wrong.

Think back again to the earlier description of the Lerp function.

img

Your t needs to be passed as a percentage. You’ll need to calculate the percentage of time that has passed since you began your Lerp, and then move your object that far between aand b.

Over time, your object’s position is going to move, so you need that reference to where it began in order to work out where it should be at any given t.

Change the Vector3.Lerp call to use a percentage of time.

transform.position = Vector3.Lerp(startPos,
                                  target.position,
                                  elapsedTime / time);

Finally, add the following to the bottom of the method, after the while loop:

transform.position = target.position;

Once the loop is finished, you make sure the position is set to the correct final position.

Phew, that was a lot. But don’t worry — that’s 90% of the coding you need to do for this chapter!

Dropping the veggies into the troughs

Save your changes and return to the Unity editor. Click Play and watch as the Veggies now fall into the troughs. Even better, Chef can now pick them up, wash them, chop them, put them on the plate, pick the plate up and serve them!

img

There’s just one small issue left. The ingredients are all lying the same way in the troughs. And they, ahem, look a little dodgy when Chef picks them up.

img

The good news is that you can fix this with just three more lines of code.

Go back into Tween script again, and revisit the Lerp IEnumerator. Notice that you’re passing Transforms into this method, which means that along with the position data, you also have access to the rotation data.

Below the line where you cached the startPos, add the following:

Quaternion startRot = transform.rotation;

Just as Vector3 has its own Lerp method, so does Quaternion. Following the Vector3.Lerp call, add the following line:

transform.rotation = Quaternion.Lerp(startRot,
                                     target.rotation,
                                     elapsedTime / time);

Finally, make sure the correct rotation is set at the end of the method, just after you set the final position.

transform.rotation = target.rotation;

Save your changes and return to the Unity editor.

Click Play once more and see how the animations come to life now as the veggies rotate and move between each interaction.

img

Congratulations! You just built your first tween library. And it’s already been put to use in no less than 10 places throughout the game. This really shows the power and versatility of using such a library.

Setting up a basic animation

Sometimes you might want a very simple looping animation just to bring life to a scene. Take a look at the serving table in the scene. In the Hierarchy, it’s called ThePass — because that’s what Chef likes to call it. Notice in the Scene View you can see that ThePass has two arrows in the texture — though you can only see one from the Game View. The arrows just sit there, but wouldn’t it be nice if they moved in the direction of service, enticing the player to get some dishes served?

img

This is the perfect example of setting up a simple looping animation using Unity’s Animationcomponent, and some basic keyframe set-up, too.

Introduction to the Animator component

Expand ThePass to reveal its children, and navigate down to the Arrow child GameObject.

img

Have a look at the Inspector view for the Arrow GameObject. Notice that it has an Animatorcomponent already, but the Controller field doesn’t have a value yet.

img

With the Arrow GameObject still selected, open the Animation window by selecting WindowAnimationAnimation from the top menu. You’ll see the following window open in the editor, prompting you to create a new Animation Clip.

img

Click the Create button to create a new Animation Clip. Save it in the folder Assets / RW / Animations with the name Arrows_Scroll. Now go check that folder and see that two new files are in there — Arrow.controller was automatically created.

Double-click to open the file, and Unity will open the Animator window.

img

What you see here is an Animation Controller that calls the Arrows_Scroll Animation Clip that you just created. Select the Arrow GameObject in the Hierarchy again, and see that this controller has been auto assigned to the Animator component you looked at earlier.

img

Great! Unity has done all the hard work for you. You’ll learn more about the Animation Controller in the next chapter, but for the scrolling arrows this is all you need.

Moving the serving table arrows

OK, time to go back to that Animation window. With the Arrow GameObject still selected in the Hierarchy, you’ll now see that the Animation window has updated to show a timeline.

It will also have your new Arrows_Scroll animation selected in the drop-down.

img

To animate the arrows texture on ThePass, you can use a very simple technique with the Shader on the Material — the UV Offset.

With the Arrow GameObject still selected, look in the Inspector and find the Material at the bottom of the view. Make sure it’s expanded so you can see all the information there. Under the Main Maps section, you’ll see two Vector2 fields — one for Tiling and one for Offset.

Hover your mouse over the Y label for offset (not the number field, but the actual label), and notice the mouse pointer changes to a drag arrow. Click and drag slowly to the right, and you’ll see the number start to change. Watch what happens to the texture of the ArrowGameObject in the Scene View as you do so.

img

OK, so all you need to do is make the offset change automatically! Set the Offset Y back to 0. Take a mental note of how it looks. Now set the Offset Y to 1. How does it look now?

img

It looks as if nothing has changed. That’s because the Arrow texture is a repeating texture. In fact, you can also see that the Tiling Y value is set to 2. That’s why you can see two arrows on ThePass. If you switch the Tiling Y to 1 you can see that it doesn’t look very good.

img

OK, time to put all this knowledge to good use. Put the Tiling and Offset back to how they began. Tiling should be { 1, 2 }, and Offset should be { 0, 0 }.

Head back over to the Animation window. Click and drag on the Time Ruler at the top of the window to place the PlayHead (that thin white line) at the 1:00 mark. This will also update the Frame Counter to 60.

Then, click the Record button to prepare the Animation window for recording. Notice that the Timer Ruler and the Record button now highlight red to show you are in recording mode.

img

With the recording state active, go back to the material once more and change the Offset Yto 1. Notice these fields are also highlighted in red to show that they are now part of the recording.

img

And if you check back over in the Animation Window, you can see these properties have now been added. Because you set the Playhead to the one-second mark earlier, you now have a Keyframe added at this position, with the values you just entered.

A keyframe has also automatically been created at the beginning of the animation, which holds the default values from the scene.

img

Note: You can see that two properties have been added to the timeline, even though you only changed one value. That’s because of the type of material used for the arrows. It’s a Specular material, meaning it creates its own Emissivelighting. So while you’re animating the texture, you’re also animating the Emission Map.

Click the Record button again to stop recording. Then click the Play button in the Animationwindow to preview.

img

There you have it! Animating arrows. Only, it still doesn’t look right, does it? That’s because by default, the animation uses an Ease In-Out style. You need to change this to a Linear style so that it looks like a single, smooth, looping motion.

Modifying animation curves

It’s time to take a look at the other view inside the Animation window. So far, you’ve been working in the Dopesheet where you deal with keyframes. Now you need to switch over to the Curves editor.

Click on the Curves tab at the bottom left of the Animation window. By default, you probably won’t be able to see the curves very well.

img

To see more clearly, expand the Mesh Renderer.Material._Main Tex_ST object and select the w value. Then, using the mouse wheel to zoom in, and Shift + mouse wheel to zoom in the vertical only, get yourself into a better position to see the curve for the animation.

img

As you can see, the value change starts off slow, picks up pace and then slows down again as it reaches its final value — like a train pulling away from one station and slowing down into the next. This is known as Ease In-Out.

You can also see on the graph that there are still the key frames at zero and at one second. Click on the key at 1:00 and you’ll see a handle appear. You can use these handles at any keyframe to manipulate how the curve looks. Double-clicking on the curve will also add new keyframes for you to manipulate. Right-clicking on a key will bring up a context menu. Use that to delete any extra keys you made, and then right-click on the keyframe at 1:00. Select Both TangentsLinear.

img

Now do the same with the first key frame and you’ll end up with a straight line — a Linearanimation. Once it’s done, make sure the Arrow GameObject is still selected in the Hierarchy, and click the Play button once more in the Animation window.

You’ll see the arrow texture looping steadily across the pass.

img

Marvelous. Chef can’t wait to start serving those meals!

Key points

  • Unity has many different ways to build animations.
  • Always think about what approach is best for your situation.
  • Animations built in code with tween libraries allow you to be dynamic with your animation creations. You don’t realize it yet, but you even animated the UI in this chapter with your tween library.
  • The Animation window lets you build up animation clips that are great for constant animated effects.
  • The Curve editor lets you create all kinds of varied animations.