序列幀動畫經常用到,最直接的方式就是用Animation錄制。但某些情況下這種方式并不是太友好,需要靠代碼的方式進行序列幀動畫的實現。
代碼實現序列幀動畫,基本的思路是定義一個序列幀的數組/列表,根據時間的流逝來確定使用哪一幀并更新顯示。
NGUI的UI2DSpriteAnimation已經實現了此功能,但是它支持的目標只有Native2D的SpriteRenderer組件或者NGUI自身的UI2DSprite組件,并不支持UGUI的Image組件。
當然可以通過改寫源碼的方式來添加對Image組件的支持,不過秉著學習的目的,我這里重新寫了一個同時支持Image組件和SpriteRenderer組件的序列幀動畫播放器。
代碼如下,注釋寫的很詳細了,不再贅述。
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
|
using UnityEngine; using UnityEngine.UI; using System; /// <summary> /// 序列幀動畫播放器 /// 支持UGUI的Image和Unity2D的SpriteRenderer /// </summary> public class FrameAnimator : MonoBehaviour { /// <summary> /// 序列幀 /// </summary> public Sprite[] Frames{ get { return frames; } set { frames = value; } } [SerializeField] private Sprite[] frames = null ; /// <summary> /// 幀率,為正時正向播放,為負時反向播放 /// </summary> public float Framerate { get { return framerate; } set { framerate = value; } } [SerializeField] private float framerate = 20.0f; /// <summary> /// 是否忽略timeScale /// </summary> public bool IgnoreTimeScale{ get { return ignoreTimeScale; } set { ignoreTimeScale = value; } } [SerializeField] private bool ignoreTimeScale = true ; /// <summary> /// 是否循環 /// </summary> public bool Loop{ get { return loop; } set { loop = value; } } [SerializeField] private bool loop = true ; //動畫曲線 [SerializeField] private AnimationCurve curve = new AnimationCurve ( new Keyframe (0, 1, 0, 0), new Keyframe (1, 1, 0, 0)); /// <summary> /// 結束事件 /// 在每次播放完一個周期時觸發 /// 在循環模式下觸發此事件時,當前幀不一定為結束幀 /// </summary> public event Action FinishEvent; //目標Image組件 private Image image; //目標SpriteRenderer組件 private SpriteRenderer spriteRenderer; //當前幀索引 private int currentFrameIndex = 0; //下一次更新時間 private float timer = 0.0f; //當前幀率,通過曲線計算而來 private float currentFramerate = 20.0f; /// <summary> /// 重設動畫 /// </summary> public void Reset () { currentFrameIndex = framerate < 0 ? frames.Length - 1 : 0; } /// <summary> /// 從停止的位置播放動畫 /// </summary> public void Play () { this .enabled = true ; } /// <summary> /// 暫停動畫 /// </summary> public void Pause () { this .enabled = false ; } /// <summary> /// 停止動畫,將位置設為初始位置 /// </summary> public void Stop () { Pause (); Reset (); } //自動開啟動畫 void Start () { image = this .GetComponent<Image> (); spriteRenderer = this .GetComponent<SpriteRenderer> (); #if UNITY_EDITOR if (image == null && spriteRenderer == null ) { Debug.LogWarning ( "No available component found. 'Image' or 'SpriteRenderer' required." , this .gameObject); } #endif } void Update () { //幀數據無效,禁用腳本 if (frames == null || frames.Length == 0) { this .enabled = false ; } else { //從曲線值計算當前幀率 float curveValue = curve.Evaluate (( float )currentFrameIndex / frames.Length); float curvedFramerate = curveValue * framerate; //幀率有效 if (curvedFramerate != 0) { //獲取當前時間 float time = ignoreTimeScale ? Time.unscaledTime : Time.time; //計算幀間隔時間 float interval = Mathf.Abs (1.0f / curvedFramerate); //滿足更新條件,執行更新操作 if (time - timer > interval) { //執行更新操作 DoUpdate (); } } #if UNITY_EDITOR else { Debug.LogWarning ( "Framerate got '0' value, animation stopped." ); } #endif } } //具體更新操作 private void DoUpdate () { //計算新的索引 int nextIndex = currentFrameIndex + ( int )Mathf.Sign (currentFramerate); //索引越界,表示已經到結束幀 if (nextIndex < 0 || nextIndex >= frames.Length) { //廣播事件 if (FinishEvent != null ) { FinishEvent (); } //非循環模式,禁用腳本 if (loop == false ) { currentFrameIndex = Mathf.Clamp (currentFrameIndex, 0, frames.Length - 1); this .enabled = false ; return ; } } //鉗制索引 currentFrameIndex = nextIndex % frames.Length; //更新圖片 if (image != null ) { image.sprite = frames [currentFrameIndex]; } else if (spriteRenderer != null ) { spriteRenderer.sprite = frames [currentFrameIndex]; } //設置計時器為當前時間 timer = ignoreTimeScale ? Time.unscaledTime : Time.time; } } |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/SerenaHaven/article/details/79273114