Tweening (“in-betweening”) animates a value from A to B over time with an easing curve: a button that pops when hovered, a coin that flies to the score counter, a door that swings open. You can write every one by hand with coroutines and Lerp, but a tween library turns ten lines into one. This guide compares the main Unity tween libraries (DOTween, PrimeTween and LeanTween), shows how to write a tiny tween helper yourself, and covers the bugs that come with tweens: killed objects, stacking tweens and timeScale.
What Tweening Gives You
A tween library handles the boring parts of procedural animation:
- Easing: dozens of curves (OutBack, InOutSine, OutElastic) with no maths.
- Sequencing: play A, then B and C together, then call a function.
- Lifecycle: kill, pause, rewind or complete tweens, and stop them automatically when the target is destroyed.
- Options: delays, loops, yoyo, unscaled time for pause menus.
Tweens are ideal for UI and “game feel” polish. For character animation, use the Animator. For authored cutscenes, use Timeline.
A Tiny DIY Tween With Async Awaitable
If you only need a few tweens, a small helper with Unity 6’s Awaitable is enough:
using System;
using UnityEngine;
public static class Tween
{
public static async Awaitable To(Func<float, bool> apply, float duration, Func<float, float> ease = null, bool unscaled = false)
{
ease ??= t => t;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += unscaled ? Time.unscaledDeltaTime : Time.deltaTime;
if (!apply(ease(Mathf.Clamp01(elapsed / duration)))) return; // target gone: stop
await Awaitable.NextFrameAsync();
}
apply(ease(1f));
}
public static float OutBack(float t) { const float c = 1.70158f; t -= 1f; return t * t * ((c + 1f) * t + c) + 1f; }
public static float OutCubic(float t) => 1f - Mathf.Pow(1f - t, 3f);
}
// Usage: pop a button in
Vector3 from = Vector3.zero, to = Vector3.one;
await Tween.To(t => { if (!button) return false; button.transform.localScale = Vector3.LerpUnclamped(from, to, t); return true; },
0.35f, Tween.OutBack);
The callback returns false when the object has been destroyed, which stops the classic “MissingReferenceException from a tween” problem. That’s about the limit of DIY before a library pays off. See our Awaitable vs coroutines guide for cancellation patterns and our Lerp guide for the maths.
DOTween vs PrimeTween vs LeanTween
| DOTween | PrimeTween | LeanTween | |
|---|---|---|---|
| Price | Free (Pro is paid) | Free | Free |
| Allocations per tween | Small (pooled) | Zero | Small |
| API style | transform.DOMove(...) extensions |
Tween.Position(transform, ...) |
LeanTween.move(...) |
| Sequences | Yes, very flexible | Yes | Basic |
| async/await | Via extensions | Built in (await a tween) | No |
| Maintenance | Mature, huge community | Actively developed | Rarely updated |
| Best for | Most projects, lots of tutorials | Performance-sensitive and mobile, new projects | Legacy projects |
Our take: DOTween if you want the most examples and integrations, PrimeTween if you’re starting fresh and care about zero garbage. Both are in our indie Asset Store tools roundup.
DOTween Essentials
After importing, run Tools > Demigiant > DOTween Utility Panel > Setup DOTween once. Then add using DG.Tweening;:
// Move, scale, rotate, fade
transform.DOMove(new Vector3(0, 2, 0), 0.5f).SetEase(Ease.OutCubic);
transform.DOScale(1.2f, 0.15f).SetLoops(2, LoopType.Yoyo);
transform.DORotate(new Vector3(0, 180, 0), 1f, RotateMode.FastBeyond360);
canvasGroup.DOFade(0f, 0.3f).SetUpdate(true); // true = ignore timeScale
// Sequence: slide in, then punch, then callback
Sequence s = DOTween.Sequence();
s.Append(panel.DOAnchorPosY(0f, 0.4f).SetEase(Ease.OutBack))
.Join(canvasGroup.DOFade(1f, 0.3f))
.Append(icon.DOPunchScale(Vector3.one * 0.2f, 0.3f))
.OnComplete(() => Debug.Log("Shown"));
// Tie tween lifetime to the GameObject
transform.DOMoveX(5f, 2f).SetLink(gameObject);
SetLink(gameObject) kills the tween when the object is destroyed. Use it on everything that can be destroyed mid-tween. To restart a hover effect cleanly, kill the old one first: transform.DOKill(); transform.DOScale(1.1f, 0.1f);.
PrimeTween Essentials
PrimeTween uses static methods, allocates nothing, and every tween can be awaited:
using PrimeTween;
Tween.Position(transform, endValue: new Vector3(0, 2, 0), duration: 0.5f, ease: Ease.OutCubic);
Tween.Scale(transform, endValue: 1.2f, duration: 0.15f, cycles: 2, cycleMode: CycleMode.Yoyo);
Tween.Alpha(canvasGroup, endValue: 0f, duration: 0.3f, useUnscaledTime: true);
Sequence.Create()
.Chain(Tween.UIAnchoredPositionY(panel, 0f, 0.4f, Ease.OutBack))
.Group(Tween.Alpha(canvasGroup, 1f, 0.3f))
.Chain(Tween.PunchScale(icon, Vector3.one * 0.2f, 0.3f));
await Tween.Position(transform, target, 1f); // continue after it finishes
Tween.StopAll(onTarget: transform);
PrimeTween stops tweens on destroyed targets automatically and has a Shake API that’s great for camera and UI shakes.
Choosing Easing Curves
| Ease | Feels like | Use for |
|---|---|---|
| Linear | Mechanical | Progress bars, rotation loops |
| OutCubic / OutQuad | Fast start, gentle stop | Most UI movement |
| InOutSine | Smooth, calm | Floating, breathing, camera moves |
| OutBack | Overshoots, then settles | Popups, buttons appearing |
| OutElastic / OutBounce | Springy, playful | Casual games, rewards, sparingly |
| InQuad / InCubic | Slow start, fast end | Things leaving the screen |
A good default rule: things entering use an Out ease, and things leaving use an In ease. Keep UI tweens short (0.15–0.4 s). Long UI animations feel sluggish.
🎨 Game feel is a craft
Easing, squash-and-stretch and timing come from animation principles. A few books on our list teach them brilliantly for game developers.
Juice Recipes
// Button hover (DOTween), kill-safe
public void OnPointerEnter(PointerEventData e) { transform.DOKill(); transform.DOScale(1.08f, 0.12f).SetEase(Ease.OutQuad).SetUpdate(true); }
public void OnPointerExit(PointerEventData e) { transform.DOKill(); transform.DOScale(1f, 0.12f).SetUpdate(true); }
// Coin flies to the score UI then bumps the counter
coin.DOMove(scoreIcon.position, 0.6f).SetEase(Ease.InBack).SetLink(coin.gameObject)
.OnComplete(() => { Destroy(coin.gameObject); scoreIcon.DOPunchScale(Vector3.one * 0.3f, 0.25f); });
// Damage flash
spriteRenderer.DOColor(Color.red, 0.05f).SetLoops(2, LoopType.Yoyo).SetLink(gameObject);
// Idle float
transform.DOMoveY(transform.position.y + 0.25f, 1.2f).SetEase(Ease.InOutSine).SetLoops(-1, LoopType.Yoyo).SetLink(gameObject);
Pair tweened health bars with the trail technique from our health bar tutorial.
Troubleshooting
MissingReferenceException after changing scenes
A tween is still running on a destroyed object. Use SetLink(gameObject) in DOTween, or kill tweens in OnDestroy. PrimeTween handles this automatically.
Hover animation grows bigger and bigger
New tweens stack on top of running ones. Kill the previous tween on that target before starting a new one.
Tweens freeze in the pause menu
The game sets Time.timeScale = 0. Use unscaled time: SetUpdate(true) in DOTween or useUnscaledTime: true in PrimeTween.
Tween fights a Rigidbody or Animator
Physics or animation overwrites the transform each frame. Tween Rigidbodies with rb.DOMove (uses MovePosition), or tween a parent object the Animator doesn’t control.
UI tween moves to the wrong place
You tweened transform.position (world space) on a UI element. Use anchored position tweens (DOAnchorPos / Tween.UIAnchoredPosition).
FAQ
Is DOTween free?
Yes. DOTween is free on the Asset Store. DOTween Pro adds a visual editor and extra integrations for a fee.
DOTween or PrimeTween?
DOTween has the largest community and most examples. PrimeTween is newer, allocation-free and supports await natively. Both are excellent choices.
Can I tween without a library?
Yes, with coroutines or async methods and Lerp plus an easing function. It’s fine for a handful of tweens.
How do I tween UI elements?
Tween RectTransform anchored positions and CanvasGroup alpha rather than world positions.
Do tweens hurt performance?
Hundreds of active tweens are fine. PrimeTween and pooled DOTween tweens create little or no garbage.
Reader questions
Asked by readers of this article and answered by AI. Behavior can differ between Unity versions, so test in yours.




Leave a Reply