Home Game Tech Tutorials

Tweening Animations in Unity with LeanTween

Learn how to use LeanTween to animate the User Interface and various GameObjects in Unity 3D by creating a Breakout game clone.

Version

  • C# 7.3, Unity 2020.3, Unity

When making games, you frequently need to animate on-screen elements to create a narrative or add that special polish to capture the player’s interest. In the latter case, these effects’ only purpose is to make the experience enjoyable.

Unity has a powerful and user-friendly animation engine that lets you animate anything your heart desires. However, some types of animation, especially the most simple ones, don’t need the full power of the animation engine. You can perform them more efficiently with a more straightforward approach: Tweening techniques.

In this tutorial, you’ll learn about tweening and how to:

  • Use tweening in GameObjects, such as assets and UI.
  • Integrate the LeanTween package in your project.
  • Apply rotation, displacement and scale effects to GameObjects in your projects.
  • Add tweening UI elements to your projects.

By the time you’re done, you’ll think about animations not only in terms of Unity’s Animator but using other types of packages as well. As a bonus, the final sample project will look great!

Final project gameplay, with ball bouncing, colors flashing and crates rotating.

Do you feel the tween?

Getting Started

Click the Download Materials button at the top or bottom of the tutorial to download the starter project. This project requires Unity 2020.3.20f1 or later.

In Assets/RW, you’ll find the assets used in this project. Look at the folder structure:

  • Input: Files used by Unity’s new input system.
  • Physics Materials: The physics materials used for the ball in the project.
  • Plugins: This folder has LeanTween installed. You’ll learn how to install LeanTween in your projects later in the tutorial.
  • Prefabs: The project’s prefabs.
  • Scenes: You’ll find the sample scene here.
  • Scripts: The C# scripts for this project.
  • Sprites: The game art, courtesy of the kenney.nl industrial platformer pack.
  • Text Mesh Pro: Files used by Text Mesh Pro where you create the UI. To learn more, make sure to check out the TextMesh Pro tutorial.

Whew, that was a lot of folders!

Open the TweenBreaker scene in Assets/RW/Scenes, then click Play to try this Breakout clone. Use the right and left arrows or the A and D buttons on the keyboard to move the paddle. Make the ball bounce around and break as many crates as you wish.

Now, it’s time to take a closer look at tweening.

Why Not Use Unity’s Animator for Everything?

That’s a great question!

Unity already has a module capable of implementing most kinds of animation, so why would you want to bring in another package? Isn’t it redundant?

The keyword here is overkill. The animation engine is too powerful for simpler tasks, and it may drain precious resources from the player’s computer.

Unity’s Animator Component has a callback function that continuously calls for every Animator on the scene. Animating GameObjects without using the Animator Component is an excellent way to keep requirements low.

What is Tweening?

Simplistically, tweening, or inbetweening, is another name for interpolation. In this operation, a property can assume any value between two limits.

For example, imagine a GameObject that translates between two points in a four second time interval, as shown in the following figure:

Moving a GameObject between two points.

You know the position at zero seconds and four seconds: As the figure shows, those are the points (0, 0, 0) and (1, 0, 0). However, to animate it properly, the computer needs to draw each frame.

By getting values between those points, the animation engine can determine that at two seconds, the GameObject should be at (0.5, 0, 0), at one second, at (0.25, 0, 0), at three seconds, (0.75, 0, 0) and so on. This simple interpolation creates animation using only simple algebraic operations.

It’s possible to make this animation a little fancier by using animation curves, also known as tweening curves. In the previous example, to get the position of an element at any given time, you had to divide the total displacement by the elapsed time and add this value to the initial position.

That isn’t the only way to reach the final value: The progression doesn’t need to be uniform. It’s time to discuss tweening curves, also called easing functions.

In this case, the interpolation is no longer linear but instead uses an arbitrary function to dictate how the element moves. For example, if you said the GameObject in the previous example should move similarly to a sine function, the velocity would be lower on the extremes of the function.

For more information on easing functions and their effects, check this reference.

Animator Component vs Tweening Techniques

When you’re choosing between the two techniques, you should consider tweening’s strengths and limitations.

Tweening is great for simple components and animations. A good rule of thumb is to use tweening whenever you want to animate something straightforward without sacrificing framerate.

But trying to create complex animations solely with tweening can quickly get out of hand. The Animator Component’s extra bulk and power is better suited for tackling these more elaborate animations, such as sprite or skeletal animation.

How much overhead is there in the Animator Component? Let’s take a moment to compare their performance.

Performance Comparison

You can use Unity’s profiler to get a better view of what’s going on behind the scenes in this Animator and tweening comparison. To allow for a better comparison, take an example scene, with sprites moving as shown in the following figure:

A sprite moving sideways.

Now, consider a scene with many of those sprites making the same movement. As their number increases, the computer requirements increase accordingly. For 300 sprites moving sideways with the animator, the Unity profiler shows:

Profiler image showing the animator consuming processing power around the 5ms mark.

The animator is that thick blue line.

That thick blue line is how much processing power Unity’s Animator component is consuming. Selecting the line at a given point shows what’s happening in Unity’s main loop:

The Main Thread in the Profiler window.

Notice that the main villain here is the Animator.Update() method. It’s taking a lot of the main thread processing time. If only there were a way to eliminate it…

That’s where LeanTween enters. LeanTween is a package that provides a lean, lightweight tweening implementation. Without the need to invoke the Animator, the whole process changes dramatically. See what the Unity profiler has to say about it:

Profiler graphic showing how the processing consumption for tweening is lower than for the animator.

The main thread is different too. Take a look:

The Main Thread in the Profiler window illustrating the performance boost of LeanTween.

And the final animation effect is the same. This demonstration proves that eliminating the Animator Component from simpler animations makes it possible to boost performance and enhance your complex animations.

Adding LeanTween to Your Project

To add LeanTween to your projects, go to its Asset Store page and add to your assets.

When you finish, it’ll appear in your package explorer window in Unity. Select it and then click Install. When prompted, click Import to add the package to your project.

Now, on to using the recently added package in your game.

Animating the Paddle With the Tween Library

The first GameObject you’ll animate is the paddle. Initially, the paddle doesn’t react to the ball collisions, which doesn’t seem realistic. After all, when things hit each other in real life, there’s always a reaction.

For the player to feel the action, the paddle needs to react to the collision. You’ll use the translation functions to displace the paddle accordingly.

Translating Objects with LeanTween

As you already learned, you can use tweening to displace game elements for a specific amount of time. With LeanTween, the move function takes care of general displacement. You specify the initial position, final position and time that the movement should take.

However, more specialized functions move the GameObject in a single axis: moveX, moveY and moveZ. In this tutorial, you’ll use the function specialized to move the paddle along the Y-axis.

Bouncing the Paddle

You need to add some displacement to the paddle and make it react to the collision with the ball. Go to Paddle.cs and replace the entire OnCollisionEnter2D() with:

private void OnCollisionEnter2D(Collision2D collision)
{
    //1
    if (collision.gameObject.tag.Equals("Cog"))
    {
        //2
        LeanTween.cancel(gameObject);
        //3
        LeanTween.moveY(gameObject, transform.position.y - 0.5f, 0.5f).setEaseShake();
    }
}

This code does three main things:

  1. This line checks if there’s a collision between the paddle and the ball (the “Cog”). In this example, the paddle can’t collide with anything else, but it’s good practice to be clear about which collision you want to handle.
  2. This function tells LeanTween to stop any other effects that might act on this GameObject. This step helps you avoid errors by ensuring no other animation effect operates simultaneously on the element.
  3. Finally, this is the line that really creates movement. If it were a sentence in English, this function would say, “move the y-axis of the gameObject half a unit downwards, over half a second”.

Now press the Play button. You’ll see the paddle bounces up and down for half a second and then returns to the initial position.

Paddle bouncing when the ball hits it.

Bounce, paddle, bounce!

However, even though the paddle moves along the Y-axis, it goes back to its initial position in the end. This happens because of the setEaseShake() appended at the end of LeanTween.moveY(). This ease curve defines that the movement should end at the same point where it started, creating the bounce effect shown on the paddle.

If you want, remove setEaseShake() and watch as the paddle gets relentlessly pounded to the bottom of the screen. But remember to add it back in when you’re done.

Paddle getting pounded off screen.

Where are you going, paddle?

Adding Character to the Ball

In the starter project, the ball bounces around, breaking the crates and bouncing off the paddle. However, you can make it a more interesting character.

Currently, the ball animation is based solely on physics: When the ball collides, it reflects and keeps moving. But, with tweening techniques, you can make the ball a little more interesting.

To create some interesting graphics, begin by changing the scale of the ball with tweening effects.

Scaling the ball

All the examples thus far were about movement. However, you can choose a value for any given property.

To illustrate that concept, replace OnCollisionEnter2D() in BallScript.cs with:

private void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.tag.Equals("Player"))
    {
        hitPosition = (ballRigidbody.position.x - collision.rigidbody.position.x) 
                / collision.collider.bounds.size.x;

            direction = new Vector2(hitPosition, 1).normalized;
            ballRigidbody.velocity = direction * BallSpeed;
    }
    // 1
    LeanTween.cancel(gameObject);
    transform.localScale = new Vector3(0.4f, 0.4f, 0.4f);
    // 2
    LeanTween.scale(gameObject, new Vector3(1.0f, 1.0f), 1.0f).setEase(LeanTweenType.punch);
}

Here’s a code breakdown:

  1. These two lines reset the GameObject behavior. In addition to LeanTween.cancel(), the ball’s scale needs to reset to avoid any error propagation. If the ball collides with another element before the animation ends, the beginning scale will be incorrect, and, in the end, the “normal” size of the ball will be modified.
  2. Again, this is the code that actually performs the operation. This time, however, you’re scaling the GameObject instead of moving it. This scales the ball from its normal size (0.4) to 1.0 and back, thanks to the punch easing function.

Press the Play button. Look at how nice the ball behaves now:

The ball bouncing on the paddle.

Personalized Easing Functions

In this example, you used a predefined easing curve for the scale operation. But setEase isn’t limited to only LeanTweenType easing curves.

This function also gives you the flexibility of drawing your own curves with Unity’s help. Add the following animationCurve variable to the BallScript class:

    public AnimationCurve animationCurve;

Then, replace:

    LeanTween.scale(gameObject, new Vector3(1.0f, 1.0f), 1.0f).setEase(LeanTweenType.punch);

With:

    LeanTween.scale(gameObject, new Vector3(1.0f, 1.0f), 1.0f).setEase(animationCurve);

Save the script changes and go to the Hierarchy window. Expand the Player Objects GameObject, select the Cog GameObject and look at the Inspector window.

Custom easing function input interface in the Unity Editor.

Here you can set your own easing curve.

You’ll see a new parameter that lets you draw your easing function graphically. You can also select a predefined curve defined by Unity.

Unity interface for selecting or editing the easing curve.

This is particularly helpful for testing because you can try various curves and behaviors for your scene elements in Play Mode. You can fine-tune your game as much as you want until the game feels exactly as you intend.

For example, if you set the curve to quickly ascend, like this:

A curve with a fast ascend rate.

The ball grows quickly in the beginning, and then plateaus, like so:

Image of the the ball growing quickly just after colliding.

However, if the curve is inverted:

The curve starting smoothly and growing fast.

The ball will begin growing slowly and pick up momentum:

Image showing the ball growing slowly in the beginning.

Because you set the ball to reset its scale for every collision in the code, it’ll work with whatever curve you choose to elaborate. However, to avoid relying on setting the size by code, you could try a curve that comes back to the initial size in the end after applying some scaling, like this:

Curve showing three peaks and returning to the beginning.

Right click the curve and add as many keys as you like to create various effects. This curve gives the ball a rubbery feeling:

Ball with bouncing scale and returning to the original size.

Now create your own rubbery curve and select it as the Animation Curve for your script.

Color-Changing Effects

In addition to Transform-based effects, like movement and scaling, you can also add color-changing effects.

In BallScript.cs, add one more line to the end of OnCollisionEnter2D() to allow for color effects:

    gameObject.LeanColor(Color.yellow, 0.5f).setEasePunch();

This final line makes the ball flash yellow for half a second, producing the following effect:

The ball changes color when it collides with other elements.

Now the ball is a character in the game.

In the case of color changing, you have the option of calling the LeanTween function directly on the GameObject, rather than having to pass the GameObject in as an argument, which is a nice bit of syntactic sugar.

Breaking the Blocks

Currently, the blocks break, but you could add more interesting behaviors to the blocks after they collide with the ball.

Open Crates.cs. You’ll see the code for OnCollisionEnter2D().

In OnCollisionEnter2D(), you find only a reference to the function that increases the score as the player breaks the crates. But, you’ll add more momentarily.

The blocks getting destroyed and simply disappearing.

The blocks just vanish…

Rotating Objects With LeanTween

By now, you may be wondering which numbers you could interpolate next. In this step, you’ll use tweening on rotations to create a destroy animation for the crates.

In the original code, when the ball collides with the crates, it simply disappears. Now you’ll use LeanTween to add a more interesting effect to it.

Replace the entire OnCollisionEnter2D() with:

private void OnCollisionEnter2D(Collision2D collision)
{
    //1
    gameObject.GetComponent<Collider2D>().enabled = false;
    //2
    LeanTween.alpha(gameObject, 0.2f, 0.6f);
    //3
    LeanTween.rotateAround(gameObject, collision.GetContact(0).normal, 250.0f, 0.6f).setDestroyOnComplete(true);
    // Increases the score 
    GameManager.Instance.IncreaseScore(1);
}

Here’s what the code does:

  1. Initially, you disable the GameObject’s collider to avoid getting additional collisions between when crate is hit and when it finally disappears from the scene.
  2. To give the illusion of the crate disappearing, you use the alpha channel to decrease the element’s opacity to 0.2 over 0.6 seconds.
  3. rotateAround() rotates the gameObject around the point where the collision happened, 250 degrees along 0.6 seconds. This creates a more responsive feel as the crate rotates around the point of contact between itself and the ball. Then, the code tells LeanTween to destroy the GameObject after the animation finishes, removing the elements from the scene just after the marked operations finishes.

Now press Play and see how cool your work looks.

Blocks rotating before getting destroyed.

Much better now!

Tweening UI Elements

You’ve come a long way in adding animation to the project. Even though the individual animations are simple, when everything comes together, the composition is awesome.

But if you look closely, you’ll notice that not everything has dynamic behavior.

The score text is still static. It counts the points correctly, but there isn’t much to it.

In Unity, UI elements are also GameObjects, so it should be simple to add some effects, right? Right!

The score increasing just changing the text.

How it is now.

Score Increase Animation

The GameManager has a reference to the text object and is responsible updating the score.

In GameManager.cs, find and replace the entire IncreaseScore(int value) with:

public void IncreaseScore(int value)
{
    gameScore += value;
    scoreDisplay.text = gameScore.ToString();

    // 1
    LeanTween.cancel(scoreDisplay.gameObject);
    scoreDisplay.transform.rotation = Quaternion.Euler(0.0f, 0.0f, 0.0f);
    scoreDisplay.transform.localScale = Vector3.one;

    // 2
    LeanTween.rotateZ(scoreDisplay.gameObject, 15.0f, 0.5f).setEasePunch();
    LeanTween.scaleX(scoreDisplay.gameObject, 1.5f, 0.5f).setEasePunch();
}

As this code has a few new functions, analyze it by blocks:

  1. This block resets the score display GameObject’s appearance. These lines stop any tweening operation acting on scoreDisplay and reset its rotation and scale to avoid any error propagation during gameplay.
  2. The functions in this block add rotation and scale effects to the scoreDisplay GameObject. Here, you declare a rotation along the Z-axis and a scale along the X-axis, with the same easing function.

As you may have realized, you can perform the same operations available for every other GameObject on the UI elements. However, while the tweening code was encapsulated inside each one, the tweening code for the score is inside the GameManager class.

Now, run the game and see your newly animated scores add up.

The score increase animation.

Much better now!

You can use LeanTween to animate other elements, not just ones where you include the script files.

Tweening the Background Color

If you press Play now, you’ll see that the game is complete, but there’s still room for improvement. The background could respond to the game actions as well. It’s the perfect opportunity to go the extra mile and add a few more interesting effects to the visuals.

Before jumping into the code, expand the Level Geometry GameObject in the Hierarchy window. Then select the Background GameObject and look at its properties in the Inspector Window.

Notice that the Sprite Renderer component has a color other than white. This helps create the illusion of three-dimensional space, with the background being at a distance from the foreground.

To act on it, you’ll need a reference to the Background GameObject. So, at the top of GameManager.cs, right below:

public GameObject Paddle;

Add two more variables to represent the reference to the Background GameObject and how much it should shake, like this:

public GameObject Background;
public float backgroundShakeRate = 2.0f;

Now, replace IncreaseScore(int value) again with the following:

public void IncreaseScore(int value)
{
    gameScore += value;
    scoreDisplay.text = gameScore.ToString();

    LeanTween.cancel(scoreDisplay.gameObject);
    scoreDisplay.transform.rotation = Quaternion.Euler(0.0f, 0.0f, 0.0f);
    scoreDisplay.transform.localScale = Vector3.one;

    LeanTween.rotateZ(scoreDisplay.gameObject, 15.0f, 0.5f).setEasePunch();
    LeanTween.scaleX(scoreDisplay.gameObject, 1.5f, 0.5f).setEasePunch();

    // 1
    LeanTween.move(Background.gameObject, Random.insideUnitCircle * backgroundShakeRate, 0.5f).setEasePunch();

    // 2
    Background.LeanColor(Color.red, 0.3f).setEasePunch().setOnComplete(() =>
        {
            Background.GetComponent<SpriteRenderer>().color = new Color(0.38f, 0.38f, 0.38f);
        });
}

Both move and LeanColor were already used for other elements. Now, you’ll use them slightly differently:

  1. This code uses LeanTween.move(). But in this case, the movement is performed in a random direction by using Random.insideUnitCircle to return a random Vector2 inside a unit circle (a circle with a radius of 1).
  2. This code shows how to define a lambda expression to execute as soon as the animation finishes. In this case, the code redefines the Background sprite color attribute to the default value to avoid changing the color, just like the ball size resets every animation round.

Don’t forget to add the reference you created in the script in the editor as well! Drag the Background GameObject from the Hierarchy window to the appropriate slot in the GameManager.

Adding a reference

Now, click Play and enjoy playing your game. Look how much better it looks compared to the initial project:

The initial project without any animation effects.

The starter project. Feels like it was 20 minutes ago…

Final project gameplay, with ball bouncing, colors flashing and crates rotating.

The final project, it just looks much better.

Where to Go From Here

From here, you can tween away anything!

You can download the completed project files by clicking the Download Materials button at the top or the bottom of this tutorial and continue exploring.

Why not get a feel for creating your custom ease curves in the Unity Editor? Try replacing some easing functions for the elements in the project and modify them as much as you like.

You can also browse the official LeanTween documentation for additional details on the API.

I hope you enjoyed this tutorial. If you have questions, comments or want to show your final project, please join the forums below!

Reviews

More like this

Contributors

Comments