Chapter 4: Tweening and Easing — Smooth Animations in Unity3D

Game Development Math for Unity3D June 2018 30 min read

What Is Tweening?

The word "tween" comes from "in-between" a term borrowed from traditional animation, where junior animators were responsible for drawing the frames between the key poses drawn by senior animators. In game development and interactive media, tweening is the process of automatically generating intermediate values between a start state and an end state over a period of time.

A tween animates a property a position, a rotation, a scale, a color, or any numeric value from point A to point B. The simplest tween is a straight linear interpolation. More expressive tweens use easing functions to shape the motion curve, giving animations personality: a bouncy UI button, a smooth camera glide, a snappy card flip.

Unity provides several native tools for tweening, and the community library DOTween is the de-facto standard for more advanced needs. This chapter covers all of them.

Native Tweening with Lerp()

Linear Interpolation (Lerp) is the mathematical foundation of all tweening. Given a start value A, an end value B, and a parameter t between 0 and 1, Lerp returns the value that is fraction t of the way from A to B.

  • t = 0 returns A
  • t = 0.5 returns the midpoint between A and B
  • t = 1 returns B

Basic Linear Movement

using UnityEngine;

public class LinearMover : MonoBehaviour
{
    public Vector3 startPosition;
    public Vector3 endPosition;
    public float duration = 2f;

    private float _elapsed = 0f;

    void Start()
    {
        startPosition = transform.position;
    }

    void Update()
    {
        if (_elapsed < duration)
        {
            _elapsed += Time.deltaTime;
            float t = Mathf.Clamp01(_elapsed / duration); // normalize to 0..1
            transform.position = Vector3.Lerp(startPosition, endPosition, t);
        }
    }
}

The "Lazy" Lerp Pattern

There is a popular shortcut where instead of tracking elapsed time, you lerp from the current position toward the target each frame. This naturally produces an ease-out effect because the distance shrinks each frame and the step size shrinks with it.

public Transform target;
public float speed = 5f;

void Update()
{
    // Each frame, move 'speed * Time.deltaTime' fraction of the remaining distance
    transform.position = Vector3.Lerp(transform.position, target.position, speed * Time.deltaTime);
}

This pattern is extremely common because it is short and produces a pleasing deceleration for free. However, it has a subtle problem: the motion is frame-rate dependent. Use the lazy pattern for visual polish where exact timing does not matter (camera follow, smooth UI hover effects).

Easing Functions

A pure linear Lerp produces mechanical, robotic motion. Real-world objects accelerate and decelerate. Easing functions transform the linear parameter t into a curved value, shaping the speed profile of the animation.

The three fundamental families are:

  • EaseIn: slow start, fast end like a car pulling away.
  • EaseOut: fast start, slow end like a car braking to a stop.
  • EaseInOut: slow start, slow end, fast middle the most natural-looking motion for most UI animations.

Using an Easings Helper Class

// Example Easings.cs (excerpt add the full class to your project)
public static class Easings
{
    // Cubic ease in: accelerates from zero
    public static float EaseInCubic(float t)
    {
        return t * t * t;
    }

    // Cubic ease out: decelerates to zero
    public static float EaseOutCubic(float t)
    {
        float f = t - 1f;
        return f * f * f + 1f;
    }

    // Cubic ease in-out
    public static float EaseInOutCubic(float t)
    {
        if (t < 0.5f)
            return 4f * t * t * t;
        else
        {
            float f = (2f * t) - 2f;
            return 0.5f * f * f * f + 1f;
        }
    }

    // Elastic ease out: spring-like overshoot
    public static float EaseOutElastic(float t)
    {
        float p = 0.3f;
        return Mathf.Pow(2f, -10f * t) * Mathf.Sin((t - p / 4f) * (2f * Mathf.PI) / p) + 1f;
    }
}

Applying an Easing to a Lerp

using UnityEngine;

public class EasedMover : MonoBehaviour
{
    public Vector3 startPosition;
    public Vector3 endPosition;
    public float duration = 1.5f;

    private float _elapsed = 0f;

    void Start()
    {
        startPosition = transform.position;
    }

    void Update()
    {
        if (_elapsed < duration)
        {
            _elapsed += Time.deltaTime;
            float tLinear = Mathf.Clamp01(_elapsed / duration);

            // Apply easing to the linear t
            float tEased = Easings.EaseInOutCubic(tLinear);

            transform.position = Vector3.Lerp(startPosition, endPosition, tEased);
        }
    }
}

The pattern is always the same: compute a linear t, transform it through an easing function, then use the result as the Lerp parameter. You can swap the easing function without touching any other code.

Lerp and AnimationCurve

Unity's AnimationCurve class lets you define a custom curve using a visual curve editor in the Inspector. This is extremely powerful for designers and artists who want to fine-tune animation timing without writing code.

using UnityEngine;

public class AnimationCurveMover : MonoBehaviour
{
    public Vector3 startPosition;
    public Vector3 endPosition;
    public float duration = 1f;

    // Edit this curve visually in the Inspector
    public AnimationCurve curve = AnimationCurve.EaseInOut(0f, 0f, 1f, 1f);

    private float _elapsed = 0f;

    void Start()
    {
        startPosition = transform.position;
    }

    void Update()
    {
        if (_elapsed < duration)
        {
            _elapsed += Time.deltaTime;
            float normalizedTime = Mathf.Clamp01(_elapsed / duration); // 0..1

            // Sample the curve at the normalized time
            float curveValue = curve.Evaluate(normalizedTime);

            transform.position = Vector3.Lerp(startPosition, endPosition, curveValue);
        }
    }
}

Installing DOTween

DOTween is the most popular tweening library for Unity, created by Daniele Giardini (Demigiant). It is available in two versions: DOTween (free) and DOTween Pro (paid, adds visual animation tool).

Installation Steps

  1. Download DOTween from the Unity Asset Store (search "DOTween") or from dotween.demigiant.com.
  2. Import the package into your Unity project.
  3. Unity will prompt you to run the DOTween Setup Wizard do this.
  4. Add using DG.Tweening; at the top of any script that uses DOTween.

How to Use DOTween

DOTween extends Unity's built-in types with extension methods. You call a tween method directly on a Transform, Rigidbody, Material, or other component.

Basic Transform Tweens

using UnityEngine;
using DG.Tweening;

public class DOTweenExamples : MonoBehaviour
{
    public Vector3 targetPosition = new Vector3(5f, 0f, 0f);
    public Vector3 targetRotation = new Vector3(0f, 180f, 0f);
    public Vector3 targetScale    = new Vector3(2f, 2f, 2f);

    void Start()
    {
        // Move to world position in 1 second
        transform.DOMove(targetPosition, 1f);

        // Rotate to Euler angles in 1 second
        transform.DORotate(targetRotation, 1f);

        // Scale to target scale in 0.5 seconds
        transform.DOScale(targetScale, 0.5f);
    }
}

Method Chaining

transform
    .DOMove(new Vector3(5f, 0f, 0f), 1.5f)
    .SetEase(Ease.OutBounce)           // apply a bounce easing
    .SetDelay(0.5f)                    // wait 0.5s before starting
    .SetLoops(3, LoopType.Yoyo)        // repeat 3 times, alternating direction
    .OnStart(() => Debug.Log("Tween started"))
    .OnComplete(() => Debug.Log("Tween finished"));

Sequences

A DOTween Sequence lets you chain multiple tweens in order, run some in parallel, and insert delays or callbacks all without nested coroutines.

void PlayIntroAnimation()
{
    Sequence seq = DOTween.Sequence();

    // Step 1: fade in (0.3 seconds)
    seq.Append(canvasGroup.DOFade(1f, 0.3f));

    // Step 2 (runs alongside step 1): slide in from left
    seq.Join(transform.DOLocalMove(Vector3.zero, 0.3f).SetEase(Ease.OutBack));

    // Step 3: wait a moment
    seq.AppendInterval(0.5f);

    // Step 4: scale up slightly and back
    seq.Append(transform.DOScale(1.05f, 0.1f));
    seq.Append(transform.DOScale(1f, 0.1f));

    // Callback when all done
    seq.OnComplete(() => Debug.Log("Intro done"));

    seq.Play();
}

Killing Tweens

Always kill tweens on an object before starting a new one on the same property, especially if the tween can be triggered repeatedly.

// Kill all tweens on this transform, then start a new one
transform.DOKill();
transform.DOMove(newTarget, 0.5f).SetEase(Ease.OutQuad);

Choosing the Right Tool

  • Simple one-off smooth movement with no timing requirements use the lazy Lerp pattern
  • Precisely timed animation with standard easing use an easing helper class with tracked elapsed time
  • Designer-configurable animation timing use AnimationCurve in the Inspector
  • Complex sequences, callbacks, Material/Light/UI animations, elastic/bounce effects use DOTween
  • Character or gameplay animations keyed to specific poses use Unity's Animator component with Animation Clips

Summary

Tweening is one of the highest-impact skills in game UI and interactive experience development.

  • Use Vector3.Lerp, Quaternion.Lerp, and Mathf.Lerp for simple interpolation
  • Apply easing functions to the t parameter to shape the motion curve
  • Use AnimationCurve when designers need visual control over timing
  • Install DOTween for sequences, callbacks, chaining, and animating any property with minimal code
  • Kill tweens before starting new ones on the same property
Unity3D Tweening Easing Animation DOTween Lerp C#

Need help with Unity3D development?

I'm a senior developer with 16+ years experience, including AAA projects at Ubisoft. Let's discuss how I can help with your game or interactive project.

Start a Conversation