参考DOTween,试着用Unity的协程实现一个简易的动画工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace MyGameFramework.Tools {
public class UnityDoTween : MonoBehaviour {

// 存储所有运行中的协程,用于清理
private static List<Coroutine> activeCoroutines = new List<Coroutine>();
private static MonoBehaviour coroutineRunner;

// 初始化方法(兼容 DOTween.Init 调用)
public static void Init(bool recycleAllByDefault = false, bool useSafeMode = true) {
// Unity 原生实现不需要特殊初始化
activeCoroutines.Clear();
}

// 清理方法(兼容 DOTween.Clear 调用)
public static void Clear(bool destroy = false) {
// 停止所有记录的协程
if (coroutineRunner != null) {
foreach (var coroutine in activeCoroutines) {
if (coroutine != null) {
coroutineRunner.StopCoroutine(coroutine);
}
}
}
activeCoroutines.Clear();
}

// 获取协程运行器
private static MonoBehaviour GetCoroutineRunner() {
if (coroutineRunner == null) {
// 尝试找到一个 MonoBehaviour 来运行协程
coroutineRunner = UnityEngine.Object.FindFirstObjectByType<MonoBehaviour>();
}
return coroutineRunner;
}

// 延迟调用方法(兼容 DOVirtual.DelayedCall)
public static void DelayedCall(float delay, Action callback) {
var runner = GetCoroutineRunner();
if (runner != null) {
var coroutine = runner.StartCoroutine(DODelay(delay, callback));
activeCoroutines.Add(coroutine);
}
}
// 缓动类型枚举
public enum EaseType {
Linear,
InQuad,
OutQuad,
InOutQuad,
InCubic,
OutCubic,
InOutCubic
}

// 获取缓动值
private static float GetEaseValue(float t, EaseType easeType) {
switch (easeType) {
case EaseType.InQuad:
return t * t;
case EaseType.OutQuad:
return t * (2 - t);
case EaseType.InOutQuad:
return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t;
case EaseType.InCubic:
return t * t * t;
case EaseType.OutCubic:
return (--t) * t * t + 1;
case EaseType.InOutCubic:
return t < 0.5f ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
case EaseType.Linear:
default:
return t;
}
}

// 移动动画
public static IEnumerator DOMove(Transform target, Vector3 endValue, float duration ,float delay = 0f, bool from = false,Action onComplete = null, EaseType easeType = EaseType.Linear) {
if(delay >0)
yield return new WaitForSeconds(delay);
Vector3 startValue = target.position;
float elapsedTime = 0f;

if (from) {
startValue = endValue;
endValue = target.position;
}
while (elapsedTime < duration) {
float t = GetEaseValue(elapsedTime / duration, easeType);
target.position = Vector3.Lerp(startValue, endValue, t);
elapsedTime += Time.deltaTime;
yield return null;
}

target.position = endValue;
onComplete?.Invoke();
}

// 本地移动动画
public static IEnumerator DOLocalMove(Transform target, Vector3 endValue, float duration ,float delay = 0f, bool from = false,Action onComplete = null, EaseType easeType = EaseType.Linear) {
if(delay >0)
yield return new WaitForSeconds(delay);
Vector3 startValue = target.localPosition;
float elapsedTime = 0f;

if (from) {
startValue = endValue;
endValue = target.localPosition;
}

while (elapsedTime < duration) {
float t = GetEaseValue(elapsedTime / duration, easeType);
target.localPosition = Vector3.Lerp(startValue, endValue, t);
elapsedTime += Time.deltaTime;
yield return null;
}

target.localPosition = endValue;
onComplete?.Invoke();
}

// 本地Y轴移动动画
public static IEnumerator DOLocalMoveY(Transform target, float endValue, float duration, float delay = 0f, Action onComplete = null, EaseType easeType = EaseType.Linear) {
if(delay > 0)
yield return new WaitForSeconds(delay);
float startValue = target.localPosition.y;
float elapsedTime = 0f;

while (elapsedTime < duration) {
float t = GetEaseValue(elapsedTime / duration, easeType);
float currentY = Mathf.Lerp(startValue, endValue, t);
target.localPosition = new Vector3(target.localPosition.x, currentY, target.localPosition.z);
elapsedTime += Time.deltaTime;
yield return null;
}

target.localPosition = new Vector3(target.localPosition.x, endValue, target.localPosition.z);
onComplete?.Invoke();
}

// 旋转动画
public static IEnumerator DORotate(Transform target, Vector3 endValue, float duration) {
Quaternion startRotation = target.rotation;
Quaternion endRotation = Quaternion.Euler(endValue);
float elapsedTime = 0f;

while (elapsedTime < duration) {
target.rotation = Quaternion.Lerp(startRotation, endRotation, elapsedTime / duration);
elapsedTime += Time.deltaTime;
yield return null;
}

target.rotation = endRotation;
}

public static IEnumerator DORotate(Transform target, Quaternion endValue, float duration) {
Quaternion startRotation = target.rotation;
float elapsedTime = 0f;

while (elapsedTime < duration) {
target.rotation = Quaternion.Lerp(startRotation, endValue, elapsedTime / duration);
elapsedTime += Time.deltaTime;
yield return null;
}

target.rotation = endValue;
}

// 本地旋转动画
public static IEnumerator DOLocalRotate(Transform target, Vector3 endValue, float duration) {
Quaternion startRotation = target.localRotation;
Quaternion endRotation = Quaternion.Euler(endValue);
float elapsedTime = 0f;

while (elapsedTime < duration) {
target.localRotation = Quaternion.Lerp(startRotation, endRotation, elapsedTime / duration);
elapsedTime += Time.deltaTime;
yield return null;
}

target.localRotation = endRotation;
}

// 缩放动画
public static IEnumerator DOScale(Transform target, Vector3 endValue, float duration,float delay = 0f, bool from = false,Action onComplete = null, EaseType easeType = EaseType.Linear) {
if(delay >0)
yield return new WaitForSeconds(delay);
Vector3 startValue = target.localScale;
float elapsedTime = 0f;

if (from) {
startValue = endValue;
endValue = target.localScale;
}
while (elapsedTime < duration) {
float t = GetEaseValue(elapsedTime / duration, easeType);
target.localScale = Vector3.Lerp(startValue, endValue, t);
elapsedTime += Time.deltaTime;
yield return null;
}

target.localScale = endValue;
onComplete?.Invoke();
}

// UI透明度动画
public static IEnumerator DOFade(CanvasGroup canvasGroup, float endValue, float duration, float delay = 0f, bool from = false,Action onComplete = null) {
if(delay >0)
yield return new WaitForSeconds(delay);
float startValue = canvasGroup.alpha;
float elapsedTime = 0f;
if (from) {
startValue = endValue;
endValue = canvasGroup.alpha;
}
while (elapsedTime < duration) {
canvasGroup.alpha = Mathf.Lerp(startValue, endValue, elapsedTime / duration);
elapsedTime += Time.deltaTime;
yield return null;
}

canvasGroup.alpha = endValue;
}

// Image颜色动画
public static IEnumerator DOColor(Image image, Color endValue, float duration) {
Color startValue = image.color;
float elapsedTime = 0f;

while (elapsedTime < duration) {
image.color = Color.Lerp(startValue, endValue, elapsedTime / duration);
elapsedTime += Time.deltaTime;
yield return null;
}
image.color = endValue;
}

// 数值动画
public static IEnumerator DOFloat(System.Action<float> onValueChanged, float startValue, float endValue,
float duration) {
float elapsedTime = 0f;

while (elapsedTime < duration) {
float currentValue = Mathf.Lerp(startValue, endValue, elapsedTime / duration);
onValueChanged?.Invoke(currentValue);
elapsedTime += Time.deltaTime;
yield return null;
}

onValueChanged?.Invoke(endValue);
}

// 带缓动函数的移动
public static IEnumerator DOMoveEase(Transform target, Vector3 endValue, float duration,
AnimationCurve easeCurve = null) {
Vector3 startValue = target.position;
float elapsedTime = 0f;

if (easeCurve == null) {
easeCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);
}

while (elapsedTime < duration) {
float t = easeCurve.Evaluate(elapsedTime / duration);
target.position = Vector3.Lerp(startValue, endValue, t);
elapsedTime += Time.deltaTime;
yield return null;
}

target.position = endValue;
}

public static IEnumerator DODelay(float delay, Action onComplete = null) {
yield return new WaitForSeconds(delay);
onComplete?.Invoke();
}
}
}