dotween

DOTween animation library — sequence composition, tween lifecycle, easing, kill strategies. CRITICAL: Always kill tweens in OnDestroy to prevent leaks and errors.

xeldaralz/helm1 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI

Agent Skills format with YAML frontmatter. Claude Code reads it as-is.

---
name: "dotween"
description: "DOTween animation library — sequence composition, tween lifecycle, easing, kill strategies. CRITICAL: Always kill tweens in OnDestroy to prevent leaks and errors."
license: "MIT"
---

# 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)`.

```csharp
// Transform tweens
transform.DOMove(new Vector3(0, 5, 0), 1f);           // World position
transform.DOLocalMove(new Vector3(0, 5, 0), 1f);      // Local position
transform.DOScale(Vector3.one * 1.5f, 0.3f);          // Scale
transform.DORotate(new Vector3(0, 180, 0), 0.5f);     // Euler rotation
transform.DOLocalRotateQuaternion(targetRot, 0.5f);   // Quaternion rotation

// UI tweens (CanvasGroup, Image, etc.)
canvasGroup.DOFade(0f, 0.5f);                         // Alpha fade
image.DOColor(Color.red, 0.2f);                       // Color change
image.DOFillAmount(1f, 1f);                            // Fill bar
rectTransform.DOAnchorPos(Vector2.zero, 0.3f);        // UI position

// Material tweens
renderer.material.DOColor(Color.white, 0.1f);
renderer.material.DOFloat(1f, "_Dissolve", 1f);

// Arbitrary value tween
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.

```csharp
Sequence seq = DOTween.Sequence();

// Append — plays after previous tween finishes
seq.Append(transform.DOMove(targetPos, 0.5f));
seq.Append(transform.DOScale(Vector3.one * 1.2f, 0.3f));

// Join — plays at the same time as the previous tween
seq.Append(transform.DOMove(targetPos, 0.5f));
seq.Join(transform.DORotate(new Vector3(0, 360, 0), 0.5f));

// Insert — plays at a specific time position in the sequence
seq.Insert(0.2f, canvasGroup.DOFade(1f, 0.3f));

// Intervals and callbacks
seq.PrependInterval(0.5f);                             // Delay before sequence starts
seq.AppendInterval(0.2f);                              // Pause between tweens
seq.AppendCallback(() => Debug.Log("Done!"));
seq.InsertCallback(1f, () => PlaySound());

// Sequence settings
seq.SetLoops(3, LoopType.Yoyo);
seq.SetUpdate(true);                                   // Unscaled time
seq.OnComplete(() => Destroy(gameObject));
```

### Nested Sequences

```csharp
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.

```csharp
transform.DOMove(target, 0.5f).SetEase(Ease.OutBounce);
transform.DOScale(1.2f, 0.2f).SetEase(Ease.OutBack);          // Pop/overshoot
transform.DOMove(target, 1f).SetEase(Ease.InOutQuad);          // Smooth start/stop
canvasGroup.DOFade(0f, 0.3f).SetEase(Ease.InQuad);             // Accelerate out
```

### 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

```csharp
[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.

```csharp
public class AnimatedElement : MonoBehaviour
{
    private Tween m_ActiveTween;

    public void PlayAnimation()
    {
        // Kill any existing tween before starting a new one
        m_ActiveTween?.Kill();
        m_ActiveTween = transform.DOScale(1.2f, 0.3f)
            .SetEase(Ease.OutBack);
    }

    private void OnDestroy()
    {
        // CRITICAL: Kill all tweens targeting this transform
        transform.DOKill();

        // If you used SetId(this), also kill by ID:
        // DOTween.Kill(this);

        // Or kill a specific stored tween:
        // m_ActiveTween?.Kill();
    }
}
```

### Kill Methods

```csharp
transform.DOKill();                  // Kill all tweens on this transform
transform.DOKill(true);              // Kill and force completion
DOTween.Kill(this);                  // Kill tweens with this object as ID
DOTween.Kill("myTween");             // Kill tweens with string ID
DOTween.KillAll();                   // Nuclear option — kill everything
tween.Kill();                        // Kill a specific tween reference
```

## Tween IDs

Tag tweens with IDs for targeted operations.

```csharp
transform.DOMove(target, 1f).SetId(this);          // Object ID
transform.DOMove(target, 1f).SetId("uiTransition"); // String ID

// Later: kill, pause, or play by ID
DOTween.Kill("uiTransition");
DOTween.Pause(this);
DOTween.Play(this);
```

## SetAutoKill and Reusable Tweens

By default, tweens auto-destroy on completion. Disable for reusable tweens.

```csharp
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();                    // Create paused, play on demand
}

public void Bounce()
{
    m_BounceTween.Restart();         // Replay from beginning
}

private void OnDestroy()
{
    m_BounceTween?.Kill();           // Must kill manually since AutoKill is off
}
```

## SetUpdate — Unscaled Time

For animations that should play during pause (Time.timeScale = 0):

```csharp
// Pause menu fade-in plays even when game is paused
canvasGroup.DOFade(1f, 0.3f).SetUpdate(true);

// Also works on sequences
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.

```csharp
// In a bootstrap MonoBehaviour or RuntimeInitializeOnLoadMethod
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void InitDOTween()
{
    DOTween.SetTweensCapacity(500, 50); // 500 tweeners, 50 sequences
}
```

## Punch and Shake — Game Juice

Short, impactful animations for feedback.

```csharp
// Punch — snaps back to original value
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);

// Shake — random oscillation
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 shake
Camera.main.DOShakePosition(0.3f, 0.5f, 14, 90, false, true);
```

## Path Tweens

Move along a series of waypoints.

```csharp
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);               // Face movement direction
```

## Common Patterns

### Button Press Feedback

```csharp
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

```csharp
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

```csharp
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

```csharp
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

```csharp
// BAD — creates a new tween every frame, massive leak
private void Update()
{
    transform.DOMove(target.position, 0.5f);
}

// GOOD — create once, update target differently
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

```csharp
// BAD — tween continues after object is destroyed
public void Animate()
{
    transform.DOScale(2f, 5f).OnComplete(() => DoSomething());
}

// GOOD — always have a kill strategy
private void OnDestroy() => transform.DOKill();
```

### DO NOT create infinite loops without a kill strategy

```csharp
// BAD — no way to stop this
transform.DORotate(new Vector3(0, 360, 0), 2f, RotateMode.FastBeyond360)
    .SetLoops(-1, LoopType.Restart);

// GOOD — store reference and kill in OnDestroy
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

```csharp
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

```csharp
Tween tween = transform.DOMove(target, 1f);

tween.Pause();
tween.Play();
tween.Restart();
tween.Rewind();
tween.Complete();             // Jump to end
tween.Goto(0.5f, true);      // Jump to time, and play
tween.PlayForward();
tween.PlayBackwards();
tween.Flip();                 // Reverse direction
```

More General & Other skills

← All General & Other skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY