当前位置:   article > 正文

Unity——用代码实现序列帧动画(动态加载)_unity rawimage 序列帧

unity rawimage 序列帧

最近的项目用SpriteRenderer+Animation的方式播放序列帧,unity编辑器里是可以正常播放没问题的,但打包出去后,发现一闪一闪的,什么玩意啊。。。然后搜索了一下文章,看到个大佬写的,换成image播放序列帧就可以了。

原文链接:https://blog.csdn.net/SerenaHaven/article/details/79273114

好了问题解决是解决了。然后我发现挂上脚本后,因为脚本上有个精灵体数组,要自己一个一个添加,比较麻烦。于是我改了下动态加载,代码如下:

  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using System;
  4. /// <summary>
  5. /// 序列帧动画播放器
  6. /// 支持UGUI的Image和Unity2D的SpriteRenderer
  7. /// </summary>
  8. public class FrameAnimator : MonoBehaviour
  9. {
  10. /// <summary>
  11. /// 序列帧数
  12. /// </summary>
  13. public int _framesCount;
  14. /// <summary>
  15. /// 序列帧名字前半部分 例如: image_1.jpg中的 image_为前半部分
  16. /// </summary>
  17. public string _framesName;
  18. /// <summary>
  19. /// resource文件夹下的文件夹路径 例如:Resources/Test/Sprites 则直接赋值 Test/Sprites 序列帧放在Sprites文件夹下就好
  20. /// </summary>
  21. public string _folderPath;
  22. /// <summary>
  23. /// 序列帧
  24. /// </summary>
  25. private Sprite[] frames = null;
  26. /// <summary>
  27. /// 帧率,为正时正向播放,为负时反向播放
  28. /// </summary>
  29. public float Framerate { get { return framerate; } set { framerate = value; } }
  30. [SerializeField] private float framerate = 20.0f;
  31. /// <summary>
  32. /// 是否忽略timeScale
  33. /// </summary>
  34. public bool IgnoreTimeScale { get { return ignoreTimeScale; } set { ignoreTimeScale = value; } }
  35. [SerializeField] private bool ignoreTimeScale = true;
  36. /// <summary>
  37. /// 是否循环
  38. /// </summary>
  39. public bool Loop { get { return loop; } set { loop = value; } }
  40. [SerializeField] private bool loop = true;
  41. //动画曲线
  42. [SerializeField] private AnimationCurve curve = new AnimationCurve(new Keyframe(0, 1, 0, 0), new Keyframe(1, 1, 0, 0));
  43. /// <summary>
  44. /// 结束事件
  45. /// 在每次播放完一个周期时触发
  46. /// 在循环模式下触发此事件时,当前帧不一定为结束帧
  47. /// </summary>
  48. public event Action FinishEvent;
  49. //目标Image组件
  50. private Image image;
  51. //目标SpriteRenderer组件
  52. private SpriteRenderer spriteRenderer;
  53. //当前帧索引
  54. private int currentFrameIndex = 0;
  55. //下一次更新时间
  56. private float timer = 0.0f;
  57. //当前帧率,通过曲线计算而来
  58. private float currentFramerate = 20.0f;
  59. /// <summary>
  60. /// 重设动画
  61. /// </summary>
  62. public void Reset()
  63. {
  64. currentFrameIndex = framerate < 0 ? frames.Length - 1 : 0;
  65. }
  66. /// <summary>
  67. /// 从停止的位置播放动画
  68. /// </summary>
  69. public void Play()
  70. {
  71. this.enabled = true;
  72. }
  73. /// <summary>
  74. /// 暂停动画
  75. /// </summary>
  76. public void Pause()
  77. {
  78. this.enabled = false;
  79. }
  80. /// <summary>
  81. /// 停止动画,将位置设为初始位置
  82. /// </summary>
  83. public void Stop()
  84. {
  85. Pause();
  86. Reset();
  87. }
  88. //自动开启动画
  89. void Start()
  90. {
  91. image = this.GetComponent<Image>();
  92. spriteRenderer = this.GetComponent<SpriteRenderer>();
  93. //初始化精灵体数组
  94. frames = new Sprite[_framesCount];
  95. for (int i = 0; i < _framesCount; i++)
  96. frames[i] = Resources.Load<Sprite>(_folderPath + "/" + _framesName + i) as Sprite;
  97. #if UNITY_EDITOR
  98. if (image == null && spriteRenderer == null)
  99. {
  100. Debug.LogWarning("No available component found. 'Image' or 'SpriteRenderer' required.", this.gameObject);
  101. }
  102. #endif
  103. }
  104. void Update()
  105. {
  106. //帧数据无效,禁用脚本
  107. if (frames == null || frames.Length == 0)
  108. {
  109. this.enabled = false;
  110. }
  111. else
  112. {
  113. //从曲线值计算当前帧率
  114. float curveValue = curve.Evaluate((float)currentFrameIndex / frames.Length);
  115. float curvedFramerate = curveValue * framerate;
  116. //帧率有效
  117. if (curvedFramerate != 0)
  118. {
  119. //获取当前时间
  120. float time = ignoreTimeScale ? Time.unscaledTime : Time.time;
  121. //计算帧间隔时间
  122. float interval = Mathf.Abs(1.0f / curvedFramerate);
  123. //满足更新条件,执行更新操作
  124. if (time - timer > interval)
  125. {
  126. //执行更新操作
  127. DoUpdate();
  128. }
  129. }
  130. #if UNITY_EDITOR
  131. else
  132. {
  133. Debug.LogWarning("Framerate got '0' value, animation stopped.");
  134. }
  135. #endif
  136. }
  137. }
  138. //具体更新操作
  139. private void DoUpdate()
  140. {
  141. //计算新的索引
  142. int nextIndex = currentFrameIndex + (int)Mathf.Sign(currentFramerate);
  143. //索引越界,表示已经到结束帧
  144. if (nextIndex < 0 || nextIndex >= frames.Length)
  145. {
  146. //广播事件
  147. if (FinishEvent != null)
  148. {
  149. FinishEvent();
  150. }
  151. //非循环模式,禁用脚本
  152. if (loop == false)
  153. {
  154. currentFrameIndex = Mathf.Clamp(currentFrameIndex, 0, frames.Length - 1);
  155. this.enabled = false;
  156. return;
  157. }
  158. }
  159. //钳制索引
  160. currentFrameIndex = nextIndex % frames.Length;
  161. //更新图片
  162. if (image != null)
  163. {
  164. image.sprite = frames[currentFrameIndex];
  165. }
  166. else if (spriteRenderer != null)
  167. {
  168. spriteRenderer.sprite = frames[currentFrameIndex];
  169. }
  170. //设置计时器为当前时间
  171. timer = ignoreTimeScale ? Time.unscaledTime : Time.time;
  172. }
  173. }

 

我想要的效果是每次显示都要从第一帧开始,但现在的问题是:如果我隐藏掉也就是setactive设为false,那么下次再激活的时候是从你刚刚隐藏的那一帧开始的。如果想要每次初始化,只需在生命周期函数OnEnable中调用一次重置的方法即可:

  1. private void OnEnable()
  2. {
  3. Reset();
  4. }

后面又更新了一版RawImage,为什么呢,图片一多了,一起拖到Resources文件夹里还是比较慢的,然后还有手动转一下精灵体,又要等好久。。。然后又改了一版。需要的自己选择!

  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. using System;
  4. /// <summary>
  5. /// 序列帧动画播放器
  6. /// 支持UGUI的Image和Unity2D的SpriteRenderer
  7. /// </summary>
  8. public class FrameAnimator : MonoBehaviour
  9. {
  10. /// <summary>
  11. /// 序列帧数
  12. /// </summary>
  13. public int _framesCount;
  14. /// <summary>
  15. /// 序列帧名字前半部分 例如: image_1.jpg中的 image_为前半部分
  16. /// </summary>
  17. public string _framesName;
  18. /// <summary>
  19. /// resource文件夹下的文件夹路径 例如:Resources/Test/Texture 则直接赋值 Test/Texture 序列帧放在Texture文件夹下就好
  20. /// </summary>
  21. public string _folderPath;
  22. /// <summary>
  23. /// 序列帧
  24. /// </summary>
  25. private Texture[] frames = null;
  26. /// <summary>
  27. /// 帧率,为正时正向播放,为负时反向播放
  28. /// </summary>
  29. public float Framerate { get { return framerate; } set { framerate = value; } }
  30. [SerializeField] private float framerate = 20.0f;
  31. /// <summary>
  32. /// 是否忽略timeScale
  33. /// </summary>
  34. public bool IgnoreTimeScale { get { return ignoreTimeScale; } set { ignoreTimeScale = value; } }
  35. [SerializeField] private bool ignoreTimeScale = true;
  36. /// <summary>
  37. /// 是否循环
  38. /// </summary>
  39. public bool Loop { get { return loop; } set { loop = value; } }
  40. [SerializeField] private bool loop = true;
  41. //动画曲线
  42. [SerializeField] private AnimationCurve curve = new AnimationCurve(new Keyframe(0, 1, 0, 0), new Keyframe(1, 1, 0, 0));
  43. /// <summary>
  44. /// 是否允许播放
  45. /// </summary>
  46. public bool _isPlayFrameAnimator { get { return IsPlayFrameAnimator; } set { IsPlayFrameAnimator = value; } }
  47. [SerializeField] private bool IsPlayFrameAnimator = false;
  48. /// <summary>
  49. /// 结束事件
  50. /// 在每次播放完一个周期时触发
  51. /// 在循环模式下触发此事件时,当前帧不一定为结束帧
  52. /// </summary>
  53. public event Action FinishEvent;
  54. //目标Image组件
  55. private RawImage image;
  56. //当前帧索引
  57. private int currentFrameIndex = 0;
  58. //下一次更新时间
  59. private float timer = 0.0f;
  60. //当前帧率,通过曲线计算而来
  61. private float currentFramerate = 20.0f;
  62. /// <summary>
  63. /// 重设动画
  64. /// </summary>
  65. public void Reset()
  66. {
  67. currentFrameIndex = framerate < 0 ? frames.Length - 1 : 0;
  68. }
  69. /// <summary>
  70. /// 从停止的位置播放动画
  71. /// </summary>
  72. public void Play()
  73. {
  74. this.enabled = true;
  75. }
  76. /// <summary>
  77. /// 暂停动画
  78. /// </summary>
  79. public void Pause()
  80. {
  81. this.enabled = false;
  82. }
  83. /// <summary>
  84. /// 停止动画,将位置设为初始位置
  85. /// </summary>
  86. public void Stop()
  87. {
  88. Pause();
  89. Reset();
  90. }
  91. //每次显示时,初始化当前帧数
  92. private void OnEnable()
  93. {
  94. Reset();
  95. }
  96. void Start()
  97. {
  98. image = GetComponent<RawImage>();
  99. //初始化精灵体数组
  100. frames = new Texture[_framesCount];
  101. for (int i = 0; i < _framesCount; i++)
  102. frames[i] = Resources.Load<Texture>(_folderPath + "/" + _framesName + i);
  103. #if UNITY_EDITOR
  104. if (image == null)
  105. {
  106. Debug.LogWarning("No available component found. 'RawImage' required.", this.gameObject);
  107. }
  108. #endif
  109. }
  110. void Update()
  111. {
  112. //帧数据无效,禁用脚本
  113. if (frames == null || frames.Length == 0)
  114. {
  115. this.enabled = false;
  116. }
  117. else
  118. {
  119. //是否允许播放
  120. if (!PlayFrameAnimator()) return;
  121. //从曲线值计算当前帧率
  122. float curveValue = curve.Evaluate((float)currentFrameIndex / frames.Length);
  123. float curvedFramerate = curveValue * framerate;
  124. //帧率有效
  125. if (curvedFramerate != 0)
  126. {
  127. //获取当前时间
  128. float time = ignoreTimeScale ? Time.unscaledTime : Time.time;
  129. //计算帧间隔时间
  130. float interval = Mathf.Abs(1.0f / curvedFramerate);
  131. //满足更新条件,执行更新操作
  132. if (time - timer > interval)
  133. {
  134. //执行更新操作
  135. DoUpdate();
  136. }
  137. }
  138. #if UNITY_EDITOR
  139. else
  140. {
  141. Debug.LogWarning("Framerate got '0' value, animation stopped.");
  142. }
  143. #endif
  144. }
  145. }
  146. /// <summary>
  147. /// 判断是否允许播放
  148. /// </summary>
  149. /// <returns>是否允许播放</returns>
  150. public bool PlayFrameAnimator()
  151. {
  152. return _isPlayFrameAnimator;
  153. }
  154. //具体更新操作
  155. private void DoUpdate()
  156. {
  157. //计算新的索引
  158. int nextIndex = currentFrameIndex + (int)Mathf.Sign(currentFramerate);
  159. //索引越界,表示已经到结束帧
  160. if (nextIndex < 0 || nextIndex >= frames.Length)
  161. {
  162. //广播事件
  163. if (FinishEvent != null)
  164. {
  165. FinishEvent();
  166. }
  167. //非循环模式,禁用脚本
  168. if (loop == false)
  169. {
  170. currentFrameIndex = Mathf.Clamp(currentFrameIndex, 0, frames.Length - 1);
  171. this.enabled = false;
  172. return;
  173. }
  174. }
  175. //钳制索引
  176. currentFrameIndex = nextIndex % frames.Length;
  177. //更新图片
  178. if (image != null)
  179. {
  180. image.texture = frames[currentFrameIndex];
  181. }
  182. //设置计时器为当前时间
  183. timer = ignoreTimeScale ? Time.unscaledTime : Time.time;
  184. }
  185. }

最近有空,之前的脚本其实倒放(原版的也不行)有问题,我一直不知道,现在重新码一遍吧~把一些不常用的功能取消了,并优化了下,要是用到什么动画曲线来调帧率的话,还是选择上面的脚本好一些

  1. using UnityEngine;
  2. using UnityEngine.UI;
  3. /*
  4. *
  5. * Writer:June(改)
  6. *
  7. * Date: 2020.6.6
  8. *
  9. * Function:序列帧动画播放器
  10. *
  11. * Remarks:支持Image和SpriteRenderer
  12. *
  13. */
  14. public class FrameAnimatorPlayScript : MonoBehaviour
  15. {
  16. /// <summary>
  17. /// 序列帧帧数
  18. /// </summary>
  19. private int _framesCount;
  20. /// <summary>
  21. /// 序列帧名字前半部分 例如: image_1.jpg中的 image_为前半部分
  22. /// </summary>
  23. public string _framesName;
  24. /// <summary>
  25. /// resource文件夹下的文件夹路径 例如:Resources/Test/Sprites 则直接赋值 Test/Sprites 序列帧放在Sprites文件夹下就好
  26. /// </summary>
  27. public string _folderPath;
  28. /// <summary>
  29. /// 序列帧精灵体数组
  30. /// </summary>
  31. private Sprite[] frames = null;
  32. /// <summary>
  33. /// 帧率
  34. /// </summary>
  35. public float Framerate { get { return framerate; } set { framerate = value; } }
  36. [Range(20, 60)]
  37. [SerializeField] private float framerate = 20.0f;
  38. /// <summary>
  39. /// 是否忽略timeScale
  40. /// </summary>
  41. public bool IgnoreTimeScale { get { return ignoreTimeScale; } set { ignoreTimeScale = value; } }
  42. [SerializeField] private bool ignoreTimeScale = true;
  43. /// <summary>
  44. /// 是否循环
  45. /// </summary>
  46. [SerializeField] private bool loop = true;
  47. public bool Loop { get { return loop; } set { loop = value; } }
  48. //目标Image组件
  49. private Image image;
  50. //目标SpriteRenderer组件
  51. private SpriteRenderer spriteRenderer;
  52. //当前帧索引
  53. private int FrameIndex = 0;
  54. //下一次更新时间
  55. private float timer = 0.0f;
  56. /// <summary>
  57. /// 是否倒放
  58. /// </summary>
  59. public bool IsRewind { get; private set; }
  60. /// <summary>
  61. /// 播放状态
  62. /// </summary>
  63. public bool IsPlay { get; private set; }
  64. /// <summary>
  65. /// 倒放
  66. /// </summary>
  67. public void PlayBackward()
  68. {
  69. //循环状态下先把循环关掉,倒放不能循环
  70. if (loop) loop = false;
  71. IsRewind = true;
  72. IsPlay = true;
  73. }
  74. /// <summary>
  75. /// 重设动画
  76. /// </summary>
  77. public void Reset()
  78. {
  79. FrameIndex = IsRewind ? _framesCount - 1 : 0;
  80. }
  81. /// <summary>
  82. /// 从停止的位置播放动画
  83. /// </summary>
  84. public void PlayForward()
  85. {
  86. IsRewind = false;
  87. IsPlay = true;
  88. }
  89. /// <summary>
  90. /// 暂停动画
  91. /// </summary>
  92. public void Pause() { IsPlay = false; }
  93. /// <summary>
  94. /// 停止动画,将位置设为初始位置
  95. /// </summary>
  96. public void Stop()
  97. {
  98. Pause();
  99. Reset();
  100. }
  101. //获取序列帧
  102. void Start()
  103. {
  104. image = GetComponent<Image>();
  105. spriteRenderer = GetComponent<SpriteRenderer>();
  106. if (image == null && spriteRenderer == null)
  107. {
  108. Debug.LogError("没找到组件", gameObject);
  109. }
  110. //读序列帧
  111. ReadFrames();
  112. }
  113. /// <summary>
  114. /// 动态读取序列帧
  115. /// </summary>
  116. private void ReadFrames()
  117. {
  118. //文件夹中的图片数
  119. _framesCount = GetCountInFolder(Application.dataPath + "/Resources/" + _folderPath);
  120. //初始化精灵体数组
  121. frames = new Sprite[_framesCount];
  122. for (int i = 0; i < frames.Length; i++)
  123. frames[i] = Resources.Load<Sprite>(_folderPath + "/" + _framesName + i) as Sprite;
  124. }
  125. /// <summary>
  126. /// 获取文件夹内文件数(不包括meta文件)
  127. /// </summary>
  128. /// <param name="_path">目标路径</param>
  129. /// <returns>文件数</returns>
  130. private int GetCountInFolder(string _path)
  131. {
  132. int _folderNumberSum = 0;
  133. string[] fileList = System.IO.Directory.GetFileSystemEntries(_path);
  134. foreach (string item in fileList)
  135. {
  136. if (!item.Contains(".meta"))
  137. _folderNumberSum++;
  138. }
  139. return _folderNumberSum;
  140. }
  141. void Update()
  142. {
  143. //帧数据无效,禁用脚本
  144. if (frames == null || frames.Length == 0 || !IsPlay) return;
  145. //帧率有效 控制帧率
  146. if (framerate != 0)
  147. {
  148. //获取当前时间
  149. float time = ignoreTimeScale ? Time.unscaledTime : Time.time;
  150. //计算帧间隔时间
  151. float interval = Mathf.Abs(1.0f / framerate);
  152. //满足更新条件,执行更新操作
  153. if (time - timer > interval)
  154. {
  155. //执行更新操作
  156. DoUpdate();
  157. }
  158. }
  159. else Debug.LogError("帧率必须大于0");
  160. }
  161. //具体更新操作
  162. private void DoUpdate()
  163. {
  164. //是否打开循环
  165. if (!loop)
  166. {
  167. FrameIndex = Mathf.Clamp(FrameIndex, 0, frames.Length - 1);
  168. //正向播放判断||倒放判断
  169. if ((!IsRewind && FrameIndex == (frames.Length - 1)) || (IsRewind && FrameIndex == 0))
  170. {
  171. IsPlay = false;
  172. return;
  173. }
  174. }
  175. //帧数赋值,更新图片
  176. if (image != null) image.sprite = frames[FrameIndex];
  177. if (spriteRenderer != null) spriteRenderer.sprite = frames[FrameIndex];
  178. if (!IsRewind) FrameIndex++;//正向播放索引自增
  179. else
  180. {
  181. FrameIndex--;//倒放,索引自减
  182. if (FrameIndex < 0) FrameIndex = 0;
  183. }
  184. FrameIndex = FrameIndex % frames.Length;
  185. //设置计时器为当前时间
  186. timer = ignoreTimeScale ? Time.unscaledTime : Time.time;
  187. }
  188. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/138302
推荐阅读
相关标签
  

闽ICP备14008679号