| name | dotween |
| description | DOTween animation library — sequence composition, tween lifecycle, easing, kill strategies. CRITICAL: Always kill tweens in OnDestroy to prevent leaks and errors. |
| globs | ["**/DOTween*","**/*Tween*.cs","**/*Animation*.cs"] |
DOTween Animation Library
DOTween (Demigiant) is the standard tweening library for Unity. It provides fluent, chainable methods for animating transforms, UI elements, materials, and arbitrary values with minimal boilerplate.
Basic Tweens
Every shortcut method follows the pattern target.DO[Property](endValue, duration).
transform.DOMove(new Vector3(0, 5, 0), 1f);
transform.DOLocalMove(new Vector3(0, 5, 0), 1f);
transform.DOScale(Vector3.one * 1.5f, 0.3f);
transform.DORotate(new Vector3(0, 180, 0), 0.5f);
transform.DOLocalRotateQuaternion(targetRot, 0.5f);
canvasGroup.DOFade(0f, 0.5f);
image.DOColor(Color.red, 0.2f);
image.DOFillAmount(1f, 1f);
rectTransform.DOAnchorPos(Vector2.zero, 0.3f);
renderer.material.DOColor(Color.white, 0.1f);
renderer.material.DOFloat(1f, "_Dissolve", 1f);
float value = 0f;
DOTween.To(() => value, x => value = x, 10f, 1f);
Sequence Composition
Sequences let you chain, overlap, and orchestrate multiple tweens as a single unit.
Sequence seq = DOTween.Sequence();
seq.Append(transform.DOMove(targetPos, 0.5f));
seq.Append(transform.DOScale(Vector3.one * 1.2f, 0.3f));
seq.Append(transform.DOMove(targetPos, 0.5f));
seq.Join(transform.DORotate(new Vector3(0, 360, 0), 0.5f));
seq.Insert(0.2f, canvasGroup.DOFade(1f, 0.3f));
seq.PrependInterval(0.5f);
seq.AppendInterval(0.2f);
seq.AppendCallback(() => Debug.Log("Done!"));
seq.InsertCallback(1f, () => PlaySound());
seq.SetLoops(3, LoopType.Yoyo);
seq.SetUpdate(true);
seq.OnComplete(() => Destroy(gameObject));
Nested Sequences
Sequence innerSeq = DOTween.Sequence();
innerSeq.Append(transform.DOScale(1.2f, 0.15f));
innerSeq.Append(transform.DOScale(1f, 0.15f));
Sequence outerSeq = DOTween.Sequence();
outerSeq.Append(transform.DOMove(targetPos, 0.5f));
outerSeq.Append(innerSeq);
Easing
Easing controls the interpolation curve. Choose based on the feel you want.
transform.DOMove(target, 0.5f).SetEase(Ease.OutBounce);
transform.DOScale(1.2f, 0.2f).SetEase(Ease.OutBack);
transform.DOMove(target, 1f).SetEase(Ease.InOutQuad);
canvasGroup.DOFade(0f, 0.3f).SetEase(Ease.InQuad);
Common Eases for Game Feel
| Ease | Use Case |
|---|
Ease.OutBack | Button press pop, element appearing with overshoot |
Ease.OutBounce | Landing, dropping items |
Ease.InOutQuad | Smooth camera moves, panel slides |
Ease.OutQuad | Natural deceleration, most general-purpose |
Ease.InBack | Element leaving with anticipation |
Ease.OutElastic | Springy, playful UI elements |
Ease.Linear | Progress bars, constant-speed movement |
Custom Ease Curves
[SerializeField] private AnimationCurve m_CustomEase;
transform.DOMove(target, 1f).SetEase(m_CustomEase);
CRITICAL: Tween Lifecycle and Kill Strategy
Always kill tweens when the owning object is destroyed. Tweens that target destroyed objects cause MissingReferenceException and memory leaks.
public class AnimatedElement : MonoBehaviour
{
private Tween m_ActiveTween;
public void PlayAnimation()
{
m_ActiveTween?.Kill();
m_ActiveTween = transform.DOScale(1.2f, 0.3f)
.SetEase(Ease.OutBack);
}
private void OnDestroy()
{
transform.DOKill();
}
}
Kill Methods
transform.DOKill();
transform.DOKill(true);
DOTween.Kill(this);
DOTween.Kill("myTween");
DOTween.KillAll();
tween.Kill();
Tween IDs
Tag tweens with IDs for targeted operations.
transform.DOMove(target, 1f).SetId(this);
transform.DOMove(target, 1f).SetId("uiTransition");
DOTween.Kill("uiTransition");
DOTween.Pause(this);
DOTween.Play(this);
SetAutoKill and Reusable Tweens
By default, tweens auto-destroy on completion. Disable for reusable tweens.
private Tween m_BounceTween;
private void Awake()
{
m_BounceTween = transform.DOScale(1.2f, 0.15f)
.SetEase(Ease.OutBack)
.SetAutoKill(false)
.SetLoops(2, LoopType.Yoyo)
.Pause();
}
public void Bounce()
{
m_BounceTween.Restart();
}
private void OnDestroy()
{
m_BounceTween?.Kill();
}
SetUpdate — Unscaled Time
For animations that should play during pause (Time.timeScale = 0):
canvasGroup.DOFade(1f, 0.3f).SetUpdate(true);
DOTween.Sequence()
.Append(panel.DOAnchorPos(Vector2.zero, 0.3f))
.SetUpdate(true);
SetCapacity — Startup Performance
Call once at application startup to pre-allocate tween capacity and avoid runtime resizing.
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void InitDOTween()
{
DOTween.SetTweensCapacity(500, 50);
}
Punch and Shake — Game Juice
Short, impactful animations for feedback.
transform.DOPunchScale(Vector3.one * 0.2f, 0.3f, 6, 0.5f);
transform.DOPunchPosition(new Vector3(0, 30, 0), 0.4f, 8, 0.5f);
transform.DOPunchRotation(new Vector3(0, 0, 15), 0.3f, 8, 0.5f);
transform.DOShakePosition(0.5f, strength: 10f, vibrato: 10, randomness: 90);
transform.DOShakeScale(0.3f, 0.5f);
transform.DOShakeRotation(0.5f, new Vector3(0, 0, 30));
Camera.main.DOShakePosition(0.3f, 0.5f, 14, 90, false, true);
Path Tweens
Move along a series of waypoints.
Vector3[] waypoints = new[]
{
new Vector3(0, 0, 0),
new Vector3(5, 2, 0),
new Vector3(10, 0, 0),
new Vector3(15, 3, 0),
};
transform.DOPath(waypoints, 3f, PathType.CatmullRom)
.SetEase(Ease.InOutQuad)
.SetLookAt(0.01f);
Common Patterns
Button Press Feedback
public class ButtonFeedback : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
private readonly Vector3 k_PressScale = Vector3.one * 0.9f;
public void OnPointerDown(PointerEventData eventData)
{
transform.DOKill();
transform.DOScale(k_PressScale, 0.1f).SetEase(Ease.OutQuad);
}
public void OnPointerUp(PointerEventData eventData)
{
transform.DOKill();
transform.DOScale(Vector3.one, 0.15f).SetEase(Ease.OutBack);
}
private void OnDestroy() => transform.DOKill();
}
Screen Transition
public class ScreenTransition : MonoBehaviour
{
[SerializeField] private CanvasGroup m_CanvasGroup;
[SerializeField] private RectTransform m_Panel;
public Tween Show()
{
m_CanvasGroup.alpha = 0f;
m_Panel.anchoredPosition = new Vector2(0, -50f);
Sequence seq = DOTween.Sequence();
seq.Append(m_CanvasGroup.DOFade(1f, 0.25f));
seq.Join(m_Panel.DOAnchorPos(Vector2.zero, 0.3f).SetEase(Ease.OutQuad));
return seq;
}
public Tween Hide()
{
Sequence seq = DOTween.Sequence();
seq.Append(m_CanvasGroup.DOFade(0f, 0.2f));
seq.Join(m_Panel.DOAnchorPos(new Vector2(0, 50f), 0.25f).SetEase(Ease.InQuad));
return seq;
}
private void OnDestroy()
{
m_CanvasGroup.DOKill();
m_Panel.DOKill();
}
}
Damage Flash
public void FlashDamage(SpriteRenderer sr)
{
sr.DOKill();
Sequence seq = DOTween.Sequence();
seq.Append(sr.DOColor(Color.red, 0.05f));
seq.Append(sr.DOColor(Color.white, 0.15f));
seq.SetId(sr);
}
Collect Animation
public void PlayCollectAnimation(Transform item, Vector3 targetUIPos)
{
Sequence seq = DOTween.Sequence();
seq.Append(item.DOScale(1.3f, 0.15f).SetEase(Ease.OutBack));
seq.Append(item.DOMove(targetUIPos, 0.4f).SetEase(Ease.InBack));
seq.Join(item.DOScale(0f, 0.3f).SetEase(Ease.InQuad));
seq.OnComplete(() => Destroy(item.gameObject));
}
Anti-Patterns
DO NOT create tweens in Update
private void Update()
{
transform.DOMove(target.position, 0.5f);
}
private Tween m_MoveTween;
public void MoveTo(Vector3 target)
{
m_MoveTween?.Kill();
m_MoveTween = transform.DOMove(target, 0.5f);
}
DO NOT forget to kill on destroy
public void Animate()
{
transform.DOScale(2f, 5f).OnComplete(() => DoSomething());
}
private void OnDestroy() => transform.DOKill();
DO NOT create infinite loops without a kill strategy
transform.DORotate(new Vector3(0, 360, 0), 2f, RotateMode.FastBeyond360)
.SetLoops(-1, LoopType.Restart);
private Tween m_SpinTween;
private void Start()
{
m_SpinTween = transform.DORotate(new Vector3(0, 360, 0), 2f, RotateMode.FastBeyond360)
.SetLoops(-1, LoopType.Restart)
.SetId(this);
}
private void OnDestroy() => DOTween.Kill(this);
Callbacks
transform.DOMove(target, 1f)
.OnStart(() => Debug.Log("Started"))
.OnUpdate(() => Debug.Log("Updating"))
.OnComplete(() => Debug.Log("Done"))
.OnKill(() => Debug.Log("Killed"))
.OnStepComplete(() => Debug.Log("Loop step done"));
Tween Control
Tween tween = transform.DOMove(target, 1f);
tween.Pause();
tween.Play();
tween.Restart();
tween.Rewind();
tween.Complete();
tween.Goto(0.5f, true);
tween.PlayForward();
tween.PlayBackwards();
tween.Flip();