从零开始玩转 DOTween:Unity 补间动画插件入门到精通全攻略
本文面向零基础小白,从「什么是 DOTween」讲起,一直讲到序列动画、路径动画、性能优化与实战案例,力争把每一个细节都讲透。建议收藏后边看边动手敲代码,跟着做一遍,你就能完全掌握它。
目录
- [一、DOTween 到底是什么?](#一、DOTween 到底是什么?)
- [二、如何安装 DOTween](#二、如何安装 DOTween)
- [三、3 分钟上手:你的第一个补间动画](#三、3 分钟上手:你的第一个补间动画)
- [四、核心概念:Tween / Sequence / Ease](#四、核心概念:Tween / Sequence / Ease)
- [五、常用 API 全面拆解](#五、常用 API 全面拆解)
- 六、链式设置方法:给动画"加配置"
- [七、Ease 缓动曲线详解](#七、Ease 缓动曲线详解)
- [八、Loop 循环模式:让动画动个不停](#八、Loop 循环模式:让动画动个不停)
- [九、Sequence 序列动画:编排动画的"导演"](#九、Sequence 序列动画:编排动画的"导演")
- 十、动画生命周期:暂停、继续、销毁
- 十一、DOVirtual:不用物体也能做动画
- 十二、性能优化与最佳实践
- 十三、常见坑与解决方案(FAQ)
- [十四、实战案例:一个完整的 UI 弹窗动画](#十四、实战案例:一个完整的 UI 弹窗动画)
- 结语
一、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(推荐)
- 打开 Unity 菜单栏
Window -> Asset Store(或使用 Package Manager 窗口切换)。 - 搜索 DOTween ,找到 Demigiant 发布的免费版,点击 Download -> Import。
- 导入完成后,顶部菜单栏会出现
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,用法完全一致。
十二、性能优化与最佳实践
动画用多了之后,性能问题会逐渐暴露,下面这些是必须养成的习惯:
-
缓存 Tween 引用并统一管理,避免产生"孤儿动画":
csharpprivate Tween _moveTween; void DoMove() { _moveTween?.Kill(); // 先杀掉旧动画,避免冲突 _moveTween = transform.DOMoveX(5f, 1f); } -
物体销毁前记得 Kill :场景切换、物体 Destroy 前调用
DOTween.KillAll()或对该物体的 TweenKill(),否则容易在回调里访问已销毁对象而报错。 -
善用
SetLink(gameObject):一行代码自动绑定生命周期,物体销毁动画自动销毁,省心防泄漏:csharptransform.DOMoveX(5f, 1f).SetLink(gameObject); -
需要复用的动画用
SetAutoKill(false)+Pause(),不要每次新建,减少 GC 压力。 -
批量操作时考虑用
DOTween.To/DOVirtual统一驱动多个对象,避免创建大量独立 Tween。 -
注意
Time.timeScale = 0的影响 :默认 Tween 会被暂停;需要暂停游戏但 UI 继续动画时,用SetUpdate(UpdateType.Normal, true)。 -
避免在
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 的核心其实就三句话:
- 一行代码出动画 :
transform.DOMove(...); - 链式加配置 :
.SetEase(...).SetLoops(...).OnComplete(...); - 复杂编排用 Sequence :
Append排队、Join同步、Insert插入。
从最简单的移动,到序列动画、路径动画、虚拟数值、性能优化,再到实战弹窗------把这篇文章里的代码都亲手敲一遍、跑一遍,你就能从"会用"进阶到"用得优雅"。遇到问题记得回看 FAQ 部分,大部分坑都已经帮你踩过了。
如果这篇文章对你有帮助,欢迎点赞收藏,也可以留言分享你在项目中用 DOTween 做过的有趣动画,我们评论区见!
(本文基于 DOTween 免费版,Unity 2019+ 均可使用;API 以官方文档为准,如有版本差异请留意官方更新说明。)