从零开始玩转 DOTween

从零开始玩转 DOTween:Unity 补间动画插件入门到精通全攻略

本文面向零基础小白,从「什么是 DOTween」讲起,一直讲到序列动画、路径动画、性能优化与实战案例,力争把每一个细节都讲透。建议收藏后边看边动手敲代码,跟着做一遍,你就能完全掌握它。


目录


一、DOTween 到底是什么?

DOTween 是 Unity 生态中最流行的补间(Tween)动画插件 ,由开发者 Demigiant 出品,目前分为免费版 DOTween(免费) 和付费版 DOTween Pro 两个版本。

什么是"补间动画"?简单说,就是你告诉引擎物体的起始状态和结束状态,引擎自动帮你计算中间每一帧的过渡过程 。比如"把 A 物体从 (0,0,0) 移动到 (10,0,0),用时 1 秒",中间这 1 秒内物体的每一帧位置,都由 DOTween 自动算好,你完全不用手写 Update 里的插值代码。

DOTween 的核心特点:

  • API 极其简洁:一行代码完成一个动画,链式调用写起来非常舒服;
  • 性能优秀 :底层做了大量优化,比手写 Lerp + Update 更省事,比 iTween 等老牌插件性能更好;
  • 功能全面:位置、旋转、缩放、颜色、透明度、文本、路径、序列、物理、相机震动......几乎覆盖所有动画需求;
  • 免费且开源度高:免费版已满足绝大多数项目需求。

和同类插件怎么选? iTween 是老牌插件但性能一般、维护较慢;LeanTween 性能也不错但生态和文档不如 DOTween;目前新项目首选基本就是 DOTween。


二、如何安装 DOTween

方式一:Asset Store(推荐)

  1. 打开 Unity 菜单栏 Window -> Asset Store(或使用 Package Manager 窗口切换)。
  2. 搜索 DOTween ,找到 Demigiant 发布的免费版,点击 Download -> Import。
  3. 导入完成后,顶部菜单栏会出现 Tools -> Demigiant -> DOTween Utility Panel。

方式二:官网 / GitHub

从 DOTween 官网(dotween.demigiant.com)下载包,解压后把 DOTween 文件夹整个拖进项目的 Assets 目录即可。

导入后的关键一步:Setup

打开 Tools -> Demigiant -> DOTween Utility Panel,点击 Setup DOTween 按钮。这一步会生成 DOTweenSettings 配置文件,并完成首次初始化。如果不做这一步,运行时可能会收到初始化相关的警告。

小提示:免费版只支持代码方式写动画;Pro 版额外提供可视化的路径编辑窗口等工具,但核心 API 完全一致,学完本文,Pro 版也能直接上手。


三、3 分钟上手:你的第一个补间动画

假设场景里有一个 Cube,挂一个脚本,在 Start 里写:

csharp 复制代码
using DG.Tweening;
using UnityEngine;

public class Demo : MonoBehaviour
{
    void Start()
    {
        // 1 秒内把物体移动到 (5, 0, 0)
        transform.DOMove(new Vector3(5f, 0f, 0f), 1f);
    }
}

运行游戏,你会发现 Cube 自己平滑地"走"过去了。这就是 DOTween 最核心的用法:对象.动画方法(目标值, 时长)。

再看几个最常用的:

csharp 复制代码
// 位移
transform.DOMove(new Vector3(5f, 0f, 0f), 1f);   // 移到指定位置(世界坐标)
transform.DOMoveX(5f, 1f);                       // 只移动 X 轴
transform.DOLocalMove(new Vector3(0f, 2f, 0f), 1f); // 本地坐标移动
transform.DOJump(targetPos, jumpPower: 3f, numJumps: 1, duration: 1f); // 跳跃

// 旋转
transform.DORotate(new Vector3(0f, 180f, 0f), 1f);  // 旋转到指定欧拉角
transform.DOLocalRotate(new Vector3(0f, 180f, 0f), 1f);
transform.DORotateQuaternion(Quaternion.Euler(0f, 90f, 0f), 1f); // 四元数

// 缩放
transform.DOScale(2f, 1f);                         // 放大 2 倍
transform.DOScale(new Vector3(1f, 2f, 1f), 1f);    // 各轴不同
transform.DOPunchScale(new Vector3(0.5f, 0.5f, 0.5f), 0.3f); // 弹性回弹

注意:这些方法是 Unity 对 Transform 等组件提供的扩展方法 ,所以脚本顶部必须写 using DG.Tweening;,否则找不到这些方法。


四、核心概念:Tween / Sequence / Ease

要精通 DOTween,必须先理解三个基础概念:

概念 含义 说明
Tween(补间) 一个独立的动画单元 例如"移动 1 秒"就是一个 Tween,它有自己的时长、缓动、循环、回调
Sequence(序列) 多个 Tween 按时间编排的组合 可以理解为"动画导演",控制动画先后、同时、延迟执行
Ease(缓动) 动画速度随时间变化的曲线 决定动画是匀速、先快后慢、回弹还是弹性等效果

每次调用 DOMove 等方法,都会返回一个 Tween 对象 (或是它的子类 Tweener)。你可以把这个返回值存下来,随时控制它:

csharp 复制代码
Tween myTween = transform.DOMove(new Vector3(5f, 0f, 0f), 1f);
myTween.Pause();   // 暂停
myTween.Play();    // 继续
myTween.Kill();    // 销毁(停止并释放)

五、常用 API 全面拆解

5.1 Transform(位移 / 旋转 / 缩放 / 跳跃)

csharp 复制代码
transform.DOMove(pos, 1f);          // 移动到目标点
transform.DOLocalMove(pos, 1f);     // 本地坐标移动
transform.DOMoveX(5f, 1f);          // 单轴移动
transform.DORotate(euler, 1f);      // 旋转到欧拉角
transform.DOScale(scale, 1f);       // 缩放到目标
transform.DOJump(pos, 2f, 1, 1f);   // 抛物线跳跃
transform.DOPunchPosition(dir, 1f); // 位置抖动(回弹)
transform.DOShakePosition(1f, 1f);  // 位置震动(如受伤反馈)
transform.DOShakeRotation(1f, 30f); // 旋转震动

5.2 UI 组件(RectTransform / CanvasGroup / Graphic)

UI 动画是开发中最常见的需求,DOTween 对这些组件也有专门支持:

csharp 复制代码
// RectTransform:锚点位置、大小
rectTransform.DOAnchorPos(new Vector2(0f, 100f), 0.5f); // 移动
rectTransform.DOSizeDelta(new Vector2(200f, 100f), 0.5f); // 改尺寸
rectTransform.DOAnchorMax(...);  // 锚点最大值
rectTransform.DOAnchorMin(...);  // 锚点最小值

// CanvasGroup:整体透明度(淡入淡出,推荐用于整个面板)
canvasGroup.DOFade(1f, 0.3f);    // 淡入
canvasGroup.DOFade(0f, 0.3f);    // 淡出

// Image / RawImage:颜色与透明度
image.DOColor(Color.red, 0.5f);
image.DOFade(0f, 0.5f);

// Text / TMP_Text
text.DOColor(Color.green, 0.5f);
text.DOFade(0f, 0.5f);
text.DOText("这是打字机效果的文字", 2f);   // 逐字打印
text.DOCounter(0, 100, 1f);               // 数字从 0 滚到 100

特别提醒:UI 物体不要用 transform.DOMove,而要用 DOAnchorPos。因为 UI 的位移由 RectTransform 控制,直接改 transform.position 会出现位置偏移、与 Canvas 适配冲突等问题。

5.3 Material(材质颜色 / 透明度)

csharp 复制代码
// 注意:需要为材质指定属性名(对应 Shader 的属性,如 _BaseColor、_Color)
renderer.material.DOColor(Color.red, "_BaseColor", 1f);
renderer.material.DOFade(0f, "_BaseColor", 1f);

5.4 Camera(相机)

csharp 复制代码
Camera.main.DOShakePosition(0.5f, 1f);   // 相机震动(打击感反馈)
Camera.main.DOShakeRotation(0.5f, 30f);
Camera.main.DOFieldOfView(60f, 1f);      // 视野缩放(狙击感)
Camera.main.DOColor(new Color(1f,0f,0f), 1f); // 背景色渐变

5.5 AudioSource(音频)

csharp 复制代码
audioSource.DOFade(0f, 1f);   // 音量渐出(配合 AudioListener.volume 可实现全局淡出)
audioSource.DOPitch(0.5f, 1f);

5.6 Rigidbody(物理)

csharp 复制代码
rigidbody.DOMove(targetPos, 1f);   // 通过物理驱动的移动
rigidbody.DORotate(targetRot, 1f);

用 Rigidbody 的 Tween 时,会通过 MovePosition/MoveRotation 驱动,适合物理场景;纯视觉位移用 Transform 即可,性能更好。

5.7 路径动画 DOPath

让物体沿着一条曲线路径运动,是做怪物巡逻、相机运镜、过场动画的神器:

csharp 复制代码
Vector3[] path = new Vector3[]
{
    new Vector3(0, 0, 0),
    new Vector3(5, 2, 0),
    new Vector3(10, 0, 0)
};

transform.DOPath(path, 3f, PathType.CatmullRom) // 平滑曲线;也可用 PathType.Linear 直线
    .SetLookAt(0.01f)                           // 让物体朝向路径方向
    .SetEase(Ease.Linear);

六、链式设置方法:给动画"加配置"

DOTween 最大的爽点之一就是链式调用:每个设置方法都返回 Tween 本身,可以无限拼接:

csharp 复制代码
transform.DOMove(new Vector3(5f, 0f, 0f), 1f)
    .SetDelay(0.5f)                 // 延迟 0.5 秒再开始
    .SetEase(Ease.OutBack)          // 缓动曲线(回弹效果)
    .SetLoops(2, LoopType.Yoyo)     // 循环 2 次,往返
    .SetRelative()                  // 相对值:相对当前位置移动
    .OnStart(() => Debug.Log("开始"))
    .OnComplete(() => Debug.Log("结束"));

常用设置方法一览:

方法 作用
SetDelay(float) 延迟开始
SetEase(Ease) 设置缓动曲线
SetLoops(int, LoopType) 设置循环次数与模式
SetRelative() 改为相对值(在当前位置基础上叠加)
SetSpeedBased() 时长参数改为"速度"(每秒移动多少单位)
SetAutoKill(bool) 是否动画结束后自动销毁(默认 true)
SetUpdate(UpdateType, bool) 指定更新类型、是否不受时间缩放影响
SetLink(GameObject) 绑定物体,物体被销毁时自动销毁动画
From() 反向:从指定值运动回当前值

补充:From() 和 SetRelative() 的区别

  • From():让物体从指定值"回到"当前值。例如 transform.DOMoveX(0f, 1f).From() 表示物体从 x=0 处移动回当前 x 位置;
  • SetRelative():目标值变成增量。例如 transform.DOMoveX(5f, 1f).SetRelative() 表示在当前 x 基础上再移动 5 个单位。

七、Ease 缓动曲线详解

缓动曲线决定动画的"节奏感"。同一个位移动画,用不同 Ease 观感天差地别:

  • Linear:匀速,适合旋转、路径运动等需要恒定速度的场景;
  • InQuad / OutQuad / InOutQuad:加速 / 减速 / 先加速后减速,最常用的基础缓动;
  • OutBack:超出目标再回弹,做"弹窗弹出""按钮点击"非常有质感;
  • OutBounce:落地弹跳,像球落地;
  • OutElastic:橡皮筋式弹性伸缩;
  • InOutSine / InOutCubic / InOutExpo:各种先慢后快组合,用于 UI 进出场。
csharp 复制代码
transform.DOScale(1.2f, 0.5f).SetEase(Ease.OutBack);  // 弹窗放大,超一点点再弹回
transform.DOScale(1f, 0.3f).SetEase(Ease.OutQuad);    // 收回时轻微减速

实践建议:UI 进场用 OutBack、退场用 InBack 或 InQuad;自然位移用 OutCubic/OutQuad;机械匀速运动用 Linear。多试多对比,动画的"高级感"一大半来自缓动。


八、Loop 循环模式:让动画动个不停

SetLoops(int loops, LoopType type) 中,loops = -1 表示无限循环。LoopType 有三种:

模式 行为
Restart 每次循环从起点重新开始(默认)
Yoyo 像悠悠球一样来回往返,非常常用
Incremental 每次循环在上次终点的基础上继续累加,适合"无限前进"
csharp 复制代码
// 呼吸灯效果:无限往返缩放
transform.DOScale(1.2f, 1f).SetLoops(-1, LoopType.Yoyo);

// 无限向前滚动
transform.DOMoveX(10f, 1f).SetLoops(-1, LoopType.Incremental);

配合循环的回调 :OnStepComplete() 在每个循环结束(非最终结束)时触发;OnComplete() 只在全部循环走完后触发。


九、Sequence 序列动画:编排动画的"导演"

当需要多个动画按顺序、同时、错峰 执行时,就要用 DOTween.Sequence():

csharp 复制代码
Sequence seq = DOTween.Sequence();

seq.Append(transform.DOMoveX(3f, 1f))          // 第一步:先右移
   .Append(transform.DOScale(1.5f, 0.5f))      // 第二步:再放大
   .AppendInterval(0.5f)                        // 第三步:停顿 0.5 秒
   .Join(transform.DORotate(new Vector3(0, 90, 0), 1f)) // 与上一步同时旋转
   .Insert(1.5f, transform.DOMoveY(2f, 1f))     // 在第 1.5 秒处插入位移
   .AppendCallback(() => Debug.Log("序列完成"));

核心方法记忆口诀:

方法 含义
Append(tween) 排队:等上一个动画结束后再执行
Join(tween) 合流:与上一个动画同时开始
Insert(time, tween) 插入:从序列的某个时间点开始执行
AppendInterval(float) 排队一段纯等待时间
AppendCallback(Action) 在队列末尾插入一个回调
InsertCallback(time, Action) 在指定时间点插入回调

注意:Join 会受 AppendInterval 影响------Interval 也是"上一个",所以 Join 在 Interval 后调用会与 Interval 结束点同步,容易踩坑,建议用 Insert 精确控制时间点。


十、动画生命周期:暂停、继续、销毁

10.1 Tween 实例控制

csharp 复制代码
Tween t = transform.DOMoveX(5f, 1f);

t.Play();       // 播放(默认自动播放,一般不用手动调)
t.Pause();      // 暂停
t.PlayForward();// 正向播放
t.PlayBackwards(); // 反向播放
t.Restart();    // 从头重播
t.Complete();   // 立即跳转到结束状态
t.Kill();       // 销毁动画(停止并释放内存,之后无法再 Play)
t.Rewind();     // 回到起点

10.2 全局控制(DOTween 静态类)

csharp 复制代码
DOTween.KillAll();      // 销毁所有动画
DOTween.PauseAll();     // 暂停所有
DOTween.PlayAll();      // 播放所有
DOTween.CompleteAll();  // 完成所有
DOTween.Clear();        // 清理全局状态(场景切换时可调用)
DOTween.Init();         // 手动初始化(一般无需调用)

10.3 防止动画"自杀":SetAutoKill

默认动画播完就自动销毁(AutoKill),此时如果你还想对 Tween 对象做 Restart() 就会报错。需要复用的动画要这样写:

csharp 复制代码
Tween t = transform.DOMoveX(5f, 1f)
    .SetAutoKill(false)   // 禁止自动销毁
    .Pause();             // 先暂停,需要时手动 Play

10.4 不受时间缩放影响:SetUpdate

csharp 复制代码
// 让动画不受 Time.timeScale 影响(例如暂停游戏时 UI 动画仍在播放)
transform.DOScale(1.2f, 0.5f).SetUpdate(UpdateType.Normal, true);

UpdateType 可选:Normal(Update 中更新)、Late(LateUpdate,适合跟随相机)、Fixed(FixedUpdate,适合物理)、Manual(手动驱动)。


十一、DOVirtual:不用物体也能做动画

DOVirtual 是不绑定任何物体的虚拟数值动画,适合做进度条、血条、相机 FOV 渐变等"纯数值过渡"需求:

csharp 复制代码
// 数字从 0 渐变到 100,持续 1 秒,每帧回调
DOVirtual.Float(0f, 100f, 1f, value =>
{
    slider.value = value;
    healthText.text = ((int)value).ToString();
});

// 更灵活的写法:使用 EasedValue
DOVirtual.Float(0f, 1f, 0.3f, value =>
{
    // value 已经是经过缓动处理的值
}).SetEase(Ease.OutQuad);

同类方法还有 DOVirtual.Int、DOVirtual.Vector3、DOVirtual.Color、DOVirtual.EasedValue,用法完全一致。


十二、性能优化与最佳实践

动画用多了之后,性能问题会逐渐暴露,下面这些是必须养成的习惯:

  1. 缓存 Tween 引用并统一管理,避免产生"孤儿动画":

    csharp 复制代码
    private Tween _moveTween;
    void DoMove() {
        _moveTween?.Kill(); // 先杀掉旧动画,避免冲突
        _moveTween = transform.DOMoveX(5f, 1f);
    }
  2. 物体销毁前记得 Kill :场景切换、物体 Destroy 前调用 DOTween.KillAll() 或对该物体的 Tween Kill(),否则容易在回调里访问已销毁对象而报错。

  3. 善用 SetLink(gameObject):一行代码自动绑定生命周期,物体销毁动画自动销毁,省心防泄漏:

    csharp 复制代码
    transform.DOMoveX(5f, 1f).SetLink(gameObject);
  4. 需要复用的动画用 SetAutoKill(false) + Pause(),不要每次新建,减少 GC 压力。

  5. 批量操作时考虑用 DOTween.To / DOVirtual 统一驱动多个对象,避免创建大量独立 Tween。

  6. 注意 Time.timeScale = 0 的影响 :默认 Tween 会被暂停;需要暂停游戏但 UI 继续动画时,用 SetUpdate(UpdateType.Normal, true)。

  7. 避免在 OnUpdate 回调里做重计算:回调每帧执行,尽量只做赋值类轻量操作。


十三、常见坑与解决方案(FAQ)

Q1:写了动画但物体不动?

  • 检查脚本顶部是否 using DG.Tweening;;
  • 检查物体是否激活(Tween 在物体 SetActive(false) 时会暂停);
  • 检查 Time.timeScale 是否为 0;
  • 确认目标值是否有变化(目标值和当前值一样自然没有动画)。

Q2:动画被"打断"或表现奇怪?

  • 同一组件上连续创建多个同类 Tween 会互相冲突。解决:先 Kill() 旧动画再创建新动画。

Q3:UI 移动偏了 / 位置不对?

  • 用了 transform.DOMove 而不是 rectTransform.DOAnchorPos。UI 位移必须用 DOAnchorPos。

Q4:物体销毁后报错 "Tween was killed" / 空引用?

  • 动画回调里访问了已销毁对象。解决:SetLink 绑定生命周期,或销毁前主动 Kill(),回调内判空。

Q5:Restart() 报错?

  • Tween 已被 AutoKill。解决:创建时 SetAutoKill(false)。

Q6:暂停游戏(timeScale=0)后 UI 动画也停了?

  • 对 UI 动画加 .SetUpdate(UpdateType.Normal, true)。

Q7:DOTween 和 DOTween Pro 有什么区别?

  • Pro 提供可视化路径编辑、快捷键、批量管理窗口等编辑辅助工具,运行时 API 与免费版完全一致,付费不影响使用习惯。

Q8:场景切换后动画相关报错?

  • 在切换场景时调用 DOTween.KillAll() 或 DOTween.Clear(),并在新场景重新初始化。

十四、实战案例:一个完整的 UI 弹窗动画

把上面所有知识串起来,做一个"弹出窗 + 遮罩"的完整示例:

csharp 复制代码
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;

public class UIPopup : MonoBehaviour
{
    [SerializeField] private CanvasGroup _mask;      // 半透明遮罩
    [SerializeField] private RectTransform _panel;   // 弹窗主体
    [SerializeField] private Text _title;            // 标题(打字机效果)

    private Sequence _showSeq;
    private Sequence _hideSeq;

    public void Show(string title)
    {
        // 弹出前重置状态
        _mask.alpha = 0f;
        _panel.localScale = Vector3.one * 0.8f;
        gameObject.SetActive(true);

        // 编排进场动画
        _showSeq = DOTween.Sequence();
        _showSeq.Append(_mask.DOFade(1f, 0.2f))                    // 遮罩淡入
            .Join(_panel.DOScale(Vector3.one, 0.3f).SetEase(Ease.OutBack)) // 弹窗回弹放大
            .Append(_title.DOText(title, 0.8f))                    // 标题打字机
            .AppendCallback(() => Debug.Log("弹窗展示完成"));
    }

    public void Hide()
    {
        _hideSeq = DOTween.Sequence();
        _hideSeq.Append(_panel.DOScale(0.8f, 0.2f).SetEase(Ease.InBack))
            .Join(_mask.DOFade(0f, 0.2f))
            .AppendCallback(() => gameObject.SetActive(false));
    }

    void OnDestroy()
    {
        // 清理,防止泄漏
        _showSeq?.Kill();
        _hideSeq?.Kill();
    }
}

这个例子用到了:Sequence 编排、Join 同时播放、Ease 缓动、Text 打字机、CanvasGroup 淡入淡出、生命周期清理------这就是一个合格 Unity 开发者写动画的完整思路。


结语

DOTween 的核心其实就三句话:

  1. 一行代码出动画 :transform.DOMove(...);
  2. 链式加配置 :.SetEase(...).SetLoops(...).OnComplete(...);
  3. 复杂编排用 Sequence :Append 排队、Join 同步、Insert 插入。

从最简单的移动,到序列动画、路径动画、虚拟数值、性能优化,再到实战弹窗------把这篇文章里的代码都亲手敲一遍、跑一遍,你就能从"会用"进阶到"用得优雅"。遇到问题记得回看 FAQ 部分,大部分坑都已经帮你踩过了。

如果这篇文章对你有帮助,欢迎点赞收藏,也可以留言分享你在项目中用 DOTween 做过的有趣动画,我们评论区见!


(本文基于 DOTween 免费版,Unity 2019+ 均可使用;API 以官方文档为准,如有版本差异请留意官方更新说明。)

相关推荐
阿松爱学习6 小时前
【Unity开发】Unity长截图功能实现
unity·c#·游戏引擎
小小数媒成员7 小时前
Rect Transform
游戏·unity·游戏引擎
淡海水13 小时前
07-01-源码-.NET Core GC总览与架构
unity·架构·c#·.netcore·gc
true...16 小时前
虚幻里面设置物体显示、隐藏的两种方式
游戏引擎·虚幻
淡海水2 天前
06-03-源码-时间分片与调度策略
unity·c#·gc
郝学胜-神的一滴2 天前
游戏设计:原型与开发 08:我们到底在为谁做游戏
程序人生·游戏·游戏引擎·游戏开发·游戏策划
五仁烧饼2 天前
动手学 Shader Graph(二):溶解与边缘发光
游戏·unity·shader·shader graph
淡海水2 天前
06-02-源码-增量标记算法实现
算法·unity·c#·gc
淡海水2 天前
06-01-源码-Incremental GC总览与设计
unity·c#·gc