Unity开发记录-Tweening功能手动实现

Tweening全称In-Betweening,是指处理两帧之间的平滑过渡。目前有DOTween插件可以快速创建In-Betweening

Tweening功能手动实现:

  1. **MyCurveSample类:**提供曲线的采样功能(给定x返回y)
  2. **MyCurve类:**用于描述曲线的类型、方向,提供静态的MyCurve类作为预设的曲线
  3. **Procedures类:**单例模式,提供CurveProcedure方法进行采样,并将采样的值(y)传入给定的Action中

常见的缓动曲线查询:

MyCurveSample类实现

  • **curveFuncs:**曲线字典,曲线类型对应Func委托,Func委托传入x值,返回y值
  • **SampleCurve:**采样函数,根据传入的曲线类型和x值返回y值
  • **CurveType和CurveDir:**提供两个枚举分别表示曲线类型和曲线方向
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
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;

public class MyCurveSample
{
/// <summary>
/// 曲线字典,根据曲线类型对应Func委托,传入x值,返回y值
/// </summary>
public static readonly Dictionary<CurveType, Func<float, float>> curveFuncs = new Dictionary<CurveType, Func<float, float>>()
{
{ CurveType.Linear,(x)=>{ return x; } },

{ CurveType.Quadratic,(x)=>{ return 1-(1-x)*(1-x); } },

{ CurveType.Cubic,(x)=>{ return 1-(1-x)*(1-x)*(1-x); } },

{ CurveType.Quartic,(x)=>{ return 1-(1-x)*(1-x)*(1-x)*(1-x); } },

{ CurveType.QuadraticDouble,(x)=>
{
return x<0.5f ? 2*x*x : 2*x*(2 - x)-1;
}
},

{ CurveType.CubicDouble,(x)=>
{
return x<0.5f ? 4*x*x*x : -4*(1-x)*(1-x)*(1-x)+1;
}
},

{ CurveType.Sine,(x)=>
{
return 0.5f*(1.0f-Mathf.Cos(x*Mathf.PI*2));
}
},

{ CurveType.SineDouble,(x)=>
{
return 0.5f*(1.0f-Mathf.Cos(x*Mathf.PI));
}
},

{ CurveType.Elastic,(x)=>
{
return x == 0 ? 0 :
x == 1 ? 1 :
Mathf.Pow(-2,-10+10*x) * Mathf.Sin( 2 * Mathf.PI * (3.33f * x - 3.58f));
}
},

{ CurveType.ElasticDouble,(x)=>
{
return x == 0 ? 0 :
x == 1 ? 1 :
x <0.5f ? Mathf.Pow(-2,-10+10*x) * Mathf.Sin( 2 * Mathf.PI * (3.33f * x - 3.58f) ) :
Mathf.Pow(2,10+(-10*x)) * Mathf.Sin( 2 * Mathf.PI * (3.33f * x - 3.58f) ) + 1;
}
},

{ CurveType.Bounce,(x)=>
{
return x < 0.363636f ? 7.5625f * x * x :
x < 0.72727f ? 7.5625f*(x-0.545454f)*(x-0.545454f)+0.75f :
x < 0.909091f ? 7.5625f * (x-0.818182f)*(x-0.818182f)+0.9375f :
7.5625f * (x-0.954545f) * (x-0.954545f)+0.98375f;
}
},

{ CurveType.BounceDouble,(x)=>
{
if(x <0.5f)
{
if(x >0.318182f)
{
x = 1.0f - x * 2.0f;
return 0.5f - 3.78125f * x * x;
}
else if(x > 0.136365f)
{
x = 0.454546f - x *2.0f;
return 0.125f - 3.78125f * x * x;
}
else if(x > 0.045455f)
{
x = 0.181818f - x * 2.0f;
return 0.03125f - 3.78125f * x * x;
}
else
{
x = 0.045455f - x * 2.0f;
return 0.007813f-3.78125f * x * x;
}
}

if(x < 0.684848f)
{
x = x * 2.0f - 1.0f;
return 3.78125f * x * x + 0.5f;
}
else if(x < 0.863635f)
{
x = x * 2.0f - 1.545454f;
return 3.78125f * x * x + 0.875f;
}
else if(x < 0.954546f)
{
x = x * 2.0f - 1.818182f;
return 3.78125f * x * x + 0.96875f;
}
else
{
x = x * 2.0f - 1.818182f;
return 3.78125f * x * x + 0.992188f;
}
}
},

};

/// <summary>
/// 曲线采样方法,传入曲线类型和x值,从曲线字典中采样得到y值
/// </summary>
/// <param name="myCurve"></param>
/// <param name="x"></param>
/// <returns></returns>
public static float SampleCurve(MyCurve myCurve,float x)
{
x = Mathf.Clamp01(x); //将x归到0~1之间
CurveType curveType = myCurve.curveType;
Func<float, float> func = curveFuncs[curveType];

//根据曲线方向返回对应的y值
switch (myCurve.curveDir)
{
case CurveDir.In:
return func(x);

case CurveDir.Out:
return 1 - func(x);

default:
return -1;
}
}
}

/// <summary>
/// 曲线类型
/// </summary>
public enum CurveType
{
[Description("线性曲线")]
Linear,
[Description("单端二次曲线")]
Quadratic,
[Description("单端三次曲线")]
Cubic,
[Description("单端四次曲线")]
Quartic,
[Description("双端二次曲线")]
QuadraticDouble,
[Description("双端三次曲线")]
CubicDouble,
[Description("单端正弦曲线")]
Sine,
[Description("双端正弦曲线")]
SineDouble,
[Description("弹性曲线")]
Elastic,
[Description("双端弹性曲线")]
ElasticDouble,
[Description("弹跳曲线")]
Bounce,
[Description("双端弹跳曲线")]
BounceDouble
}

/// <summary>
/// 曲线的方向:In:0到1;Out:1到0
/// </summary>
public enum CurveDir
{
[Description("0到1")]
In,
[Description("1到0")]
Out
}

MyCurve类

用于描述曲线的类型、方向,提供静态的MyCurve类作为预设的曲线

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
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;

public class MyCurve
{
public CurveType curveType;
public CurveDir curveDir;
public Func<float, float> func { get; private set; }

/// <summary>
/// 线性曲线
/// </summary>
public static MyCurve Linear { get { return new MyCurve(CurveType.Linear, CurveDir.In); } }
/// <summary>
/// 反向线性曲线
/// </summary>
public static MyCurve LinearReverse { get { return new MyCurve(CurveType.Linear, CurveDir.Out); } }

/// <summary>
/// 单端二次曲线
/// </summary>
public static MyCurve Quadratic { get { return new MyCurve(CurveType.Quadratic, CurveDir.In); } }
/// <summary>
/// 反向单端二次曲线
/// </summary>
public static MyCurve QuadraticReverse { get { return new MyCurve(CurveType.Quadratic, CurveDir.Out); } }

/// <summary>
/// 单端三次曲线
/// </summary>
public static MyCurve Cubic { get { return new MyCurve(CurveType.Cubic, CurveDir.In); } }
/// <summary>
/// 反向单端三次曲线
/// </summary>
public static MyCurve CubicReverse { get { return new MyCurve(CurveType.Cubic, CurveDir.Out); } }

/// <summary>
/// 单端四次曲线
/// </summary>
public static MyCurve Quartic { get { return new MyCurve(CurveType.Quartic, CurveDir.In); } }
/// <summary>
/// 反向单端四次曲线
/// </summary>
public static MyCurve QuarticReverse { get { return new MyCurve(CurveType.Quartic, CurveDir.Out); } }

/// <summary>
/// 双端二次曲线
/// </summary>
public static MyCurve QuadraticDouble { get { return new MyCurve(CurveType.QuadraticDouble, CurveDir.In); } }
/// <summary>
/// 反向双端二次曲线
/// </summary>
public static MyCurve QuadraticDoubleReverse { get { return new MyCurve(CurveType.QuadraticDouble, CurveDir.Out); } }

/// <summary>
/// 双端三次曲线
/// </summary>
public static MyCurve CubicDouble { get { return new MyCurve(CurveType.CubicDouble, CurveDir.In); } }
/// <summary>
/// 反向双端三次曲线
/// </summary>
public static MyCurve CubicDoubleReverse { get { return new MyCurve(CurveType.CubicDouble, CurveDir.Out); } }

/// <summary>
/// 单端正弦曲线
/// </summary>
public static MyCurve Sine { get { return new MyCurve(CurveType.Sine, CurveDir.In); } }
/// <summary>
/// 反向单端正弦曲线
/// </summary>
public static MyCurve SineReverse { get { return new MyCurve(CurveType.Sine, CurveDir.Out); } }

/// <summary>
/// 双端正弦曲线
/// </summary>
public static MyCurve SineDouble { get { return new MyCurve(CurveType.SineDouble, CurveDir.In); } }
/// <summary>
/// 反向双端正弦曲线
/// </summary>
public static MyCurve SineDoubleReverse { get { return new MyCurve(CurveType.SineDouble, CurveDir.Out); } }

/// <summary>
/// 弹性曲线
/// </summary>
public static MyCurve Elastic { get { return new MyCurve(CurveType.Elastic, CurveDir.In); } }
/// <summary>
/// 反向弹性曲线
/// </summary>
public static MyCurve ElasticReverse { get { return new MyCurve(CurveType.Elastic, CurveDir.Out); } }

/// <summary>
/// 双端弹性曲线
/// </summary>
public static MyCurve ElasticDouble { get { return new MyCurve(CurveType.ElasticDouble, CurveDir.In); } }
/// <summary>
/// 反向双端弹性曲线
/// </summary>
public static MyCurve ElasticDoubleReverse { get { return new MyCurve(CurveType.ElasticDouble, CurveDir.Out); } }

/// <summary>
/// 弹跳曲线
/// </summary>
public static MyCurve Bounce { get { return new MyCurve(CurveType.Bounce, CurveDir.In); } }
/// <summary>
/// 反向弹跳曲线
/// </summary>
public static MyCurve BounceReverse { get { return new MyCurve(CurveType.Bounce, CurveDir.Out); } }

/// <summary>
/// 双端弹跳曲线
/// </summary>
public static MyCurve BounceDouble { get { return new MyCurve(CurveType.BounceDouble, CurveDir.In); } }
/// <summary>
/// 反向双端弹跳曲线
/// </summary>
public static MyCurve BounceDoubleReverse { get { return new MyCurve(CurveType.BounceDouble, CurveDir.Out); } }

public MyCurve(CurveType curveType,CurveDir curveDir)
{
this.curveType = curveType;
this.curveDir = curveDir;
}

public MyCurve(Func<float,float> func)
{
this.func = func;
}

/// <summary>
/// 翻转曲线方法
/// </summary>
/// <returns></returns>
public MyCurve Reverse()
{
return new MyCurve(curveType, curveDir == CurveDir.In ? CurveDir.Out : CurveDir.In);
}
}

Procedures类

单例模式,提供CurveProcedure方法进行采样,并将采样的值(y)传入给定的Action中

CurveProcedure方法的参数:

  • MyCurve myCurve:表示曲线的类型
  • float time:表示x从0到1所需要的时间
  • Action<float> action:对于每一个采样到的y值,将其作为参数传入此action中
  • Action onComplete = null:默认参数,在x到达1之后调用,为null则不调用
  • string id = "":默认参数,用于标识
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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Procedures : MonoBehaviour
{
private static Procedures instance;

private void Awake()
{
if(instance == null)
{
instance = this;
}
else
{
Destroy(this);
}
}

/// <summary>
/// 在time时间内,x从0到1,从myCurve中采样得到y作为参数传入action中
/// </summary>
/// <param name="myCurve"></param>
/// <param name="time"></param>
/// <param name="action"></param>
/// <param name="onComplete"></param>
/// <param name="id"></param>
public void CurveProcedure(MyCurve myCurve, float time, Action<float> action, Action onComplete = null, string id = "")
{
StartCoroutine(ProcedureCoroutine(myCurve, time, action, onComplete, id));
}

private static IEnumerator ProcedureCoroutine(MyCurve myCurve, float time, Action<float> action, Action onComplete = null, string id = "")
{
float deltaTime = Time.fixedDeltaTime; //0.02f
float count = time / deltaTime; //给定时间内会经过count个deltaTime
float step = 1 / count; //x分为step次从0到达1

float x = 0;
for (int i = 0; i < count; i++)
{
action(MyCurveSample.SampleCurve(myCurve, x));
x += step;
yield return new WaitForFixedUpdate();
}

if (onComplete != null)
{
onComplete();
}
}

public static Procedures GetInstance()
{
return instance;
}
}

调用示例与效果展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class test : MonoBehaviour
{
public Vector3 startPosition;
public Vector3 endPosition;
public float times;

void Start()
{
//调用tweening实现方块移动动画
Procedures.GetInstance().CurveProcedure(MyCurve.Bounce,times,(y)=>
{
transform.position = Vector3.Lerp(startPosition, endPosition, y);
});
}
}