赞
踩
最近的项目用SpriteRenderer+Animation的方式播放序列帧,unity编辑器里是可以正常播放没问题的,但打包出去后,发现一闪一闪的,什么玩意啊。。。然后搜索了一下文章,看到个大佬写的,换成image播放序列帧就可以了。
原文链接:https://blog.csdn.net/SerenaHaven/article/details/79273114
好了问题解决是解决了。然后我发现挂上脚本后,因为脚本上有个精灵体数组,要自己一个一个添加,比较麻烦。于是我改了下动态加载,代码如下:
- using UnityEngine;
- using UnityEngine.UI;
- using System;
-
- /// <summary>
- /// 序列帧动画播放器
- /// 支持UGUI的Image和Unity2D的SpriteRenderer
- /// </summary>
- public class FrameAnimator : MonoBehaviour
- {
- /// <summary>
- /// 序列帧数
- /// </summary>
- public int _framesCount;
- /// <summary>
- /// 序列帧名字前半部分 例如: image_1.jpg中的 image_为前半部分
- /// </summary>
- public string _framesName;
- /// <summary>
- /// resource文件夹下的文件夹路径 例如:Resources/Test/Sprites 则直接赋值 Test/Sprites 序列帧放在Sprites文件夹下就好
- /// </summary>
- public string _folderPath;
- /// <summary>
- /// 序列帧
- /// </summary>
- 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>();
-
- //初始化精灵体数组
- frames = new Sprite[_framesCount];
- for (int i = 0; i < _framesCount; i++)
- frames[i] = Resources.Load<Sprite>(_folderPath + "/" + _framesName + i) as Sprite;
-
-
- #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;
- }
- }
我想要的效果是每次显示都要从第一帧开始,但现在的问题是:如果我隐藏掉也就是setactive设为false,那么下次再激活的时候是从你刚刚隐藏的那一帧开始的。如果想要每次初始化,只需在生命周期函数OnEnable中调用一次重置的方法即可:
- private void OnEnable()
- {
- Reset();
- }
后面又更新了一版RawImage,为什么呢,图片一多了,一起拖到Resources文件夹里还是比较慢的,然后还有手动转一下精灵体,又要等好久。。。然后又改了一版。需要的自己选择!
- using UnityEngine;
- using UnityEngine.UI;
- using System;
-
- /// <summary>
- /// 序列帧动画播放器
- /// 支持UGUI的Image和Unity2D的SpriteRenderer
- /// </summary>
- public class FrameAnimator : MonoBehaviour
- {
- /// <summary>
- /// 序列帧数
- /// </summary>
- public int _framesCount;
- /// <summary>
- /// 序列帧名字前半部分 例如: image_1.jpg中的 image_为前半部分
- /// </summary>
- public string _framesName;
- /// <summary>
- /// resource文件夹下的文件夹路径 例如:Resources/Test/Texture 则直接赋值 Test/Texture 序列帧放在Texture文件夹下就好
- /// </summary>
- public string _folderPath;
- /// <summary>
- /// 序列帧
- /// </summary>
- private Texture[] 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 bool _isPlayFrameAnimator { get { return IsPlayFrameAnimator; } set { IsPlayFrameAnimator = value; } }
- [SerializeField] private bool IsPlayFrameAnimator = false;
-
- /// <summary>
- /// 结束事件
- /// 在每次播放完一个周期时触发
- /// 在循环模式下触发此事件时,当前帧不一定为结束帧
- /// </summary>
- public event Action FinishEvent;
-
- //目标Image组件
- private RawImage image;
- //当前帧索引
- 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();
- }
-
- //每次显示时,初始化当前帧数
- private void OnEnable()
- {
- Reset();
- }
-
- void Start()
- {
- image = GetComponent<RawImage>();
-
- //初始化精灵体数组
- frames = new Texture[_framesCount];
- for (int i = 0; i < _framesCount; i++)
- frames[i] = Resources.Load<Texture>(_folderPath + "/" + _framesName + i);
- #if UNITY_EDITOR
- if (image == null)
- {
- Debug.LogWarning("No available component found. 'RawImage' required.", this.gameObject);
- }
- #endif
- }
-
- void Update()
- {
- //帧数据无效,禁用脚本
- if (frames == null || frames.Length == 0)
- {
- this.enabled = false;
- }
- else
- {
- //是否允许播放
- if (!PlayFrameAnimator()) return;
- //从曲线值计算当前帧率
- 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
- }
- }
-
- /// <summary>
- /// 判断是否允许播放
- /// </summary>
- /// <returns>是否允许播放</returns>
- public bool PlayFrameAnimator()
- {
- return _isPlayFrameAnimator;
- }
-
- //具体更新操作
- 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.texture = frames[currentFrameIndex];
- }
- //设置计时器为当前时间
- timer = ignoreTimeScale ? Time.unscaledTime : Time.time;
- }
- }
最近有空,之前的脚本其实倒放(原版的也不行)有问题,我一直不知道,现在重新码一遍吧~把一些不常用的功能取消了,并优化了下,要是用到什么动画曲线来调帧率的话,还是选择上面的脚本好一些
- using UnityEngine;
- using UnityEngine.UI;
-
-
- /*
- *
- * Writer:June(改)
- *
- * Date: 2020.6.6
- *
- * Function:序列帧动画播放器
- *
- * Remarks:支持Image和SpriteRenderer
- *
- */
-
-
- public class FrameAnimatorPlayScript : MonoBehaviour
- {
- /// <summary>
- /// 序列帧帧数
- /// </summary>
- private int _framesCount;
- /// <summary>
- /// 序列帧名字前半部分 例如: image_1.jpg中的 image_为前半部分
- /// </summary>
- public string _framesName;
- /// <summary>
- /// resource文件夹下的文件夹路径 例如:Resources/Test/Sprites 则直接赋值 Test/Sprites 序列帧放在Sprites文件夹下就好
- /// </summary>
- public string _folderPath;
- /// <summary>
- /// 序列帧精灵体数组
- /// </summary>
- private Sprite[] frames = null;
- /// <summary>
- /// 帧率
- /// </summary>
- public float Framerate { get { return framerate; } set { framerate = value; } }
- [Range(20, 60)]
- [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>
- [SerializeField] private bool loop = true;
- public bool Loop { get { return loop; } set { loop = value; } }
- //目标Image组件
- private Image image;
- //目标SpriteRenderer组件
- private SpriteRenderer spriteRenderer;
- //当前帧索引
- private int FrameIndex = 0;
- //下一次更新时间
- private float timer = 0.0f;
- /// <summary>
- /// 是否倒放
- /// </summary>
- public bool IsRewind { get; private set; }
- /// <summary>
- /// 播放状态
- /// </summary>
- public bool IsPlay { get; private set; }
-
-
- /// <summary>
- /// 倒放
- /// </summary>
- public void PlayBackward()
- {
- //循环状态下先把循环关掉,倒放不能循环
- if (loop) loop = false;
- IsRewind = true;
- IsPlay = true;
- }
-
- /// <summary>
- /// 重设动画
- /// </summary>
- public void Reset()
- {
- FrameIndex = IsRewind ? _framesCount - 1 : 0;
- }
-
- /// <summary>
- /// 从停止的位置播放动画
- /// </summary>
- public void PlayForward()
- {
- IsRewind = false;
- IsPlay = true;
- }
-
- /// <summary>
- /// 暂停动画
- /// </summary>
- public void Pause() { IsPlay = false; }
-
- /// <summary>
- /// 停止动画,将位置设为初始位置
- /// </summary>
- public void Stop()
- {
- Pause();
- Reset();
- }
-
- //获取序列帧
- void Start()
- {
- image = GetComponent<Image>();
- spriteRenderer = GetComponent<SpriteRenderer>();
- if (image == null && spriteRenderer == null)
- {
- Debug.LogError("没找到组件", gameObject);
- }
- //读序列帧
- ReadFrames();
- }
-
-
- /// <summary>
- /// 动态读取序列帧
- /// </summary>
- private void ReadFrames()
- {
- //文件夹中的图片数
- _framesCount = GetCountInFolder(Application.dataPath + "/Resources/" + _folderPath);
- //初始化精灵体数组
- frames = new Sprite[_framesCount];
- for (int i = 0; i < frames.Length; i++)
- frames[i] = Resources.Load<Sprite>(_folderPath + "/" + _framesName + i) as Sprite;
- }
-
-
- /// <summary>
- /// 获取文件夹内文件数(不包括meta文件)
- /// </summary>
- /// <param name="_path">目标路径</param>
- /// <returns>文件数</returns>
- private int GetCountInFolder(string _path)
- {
- int _folderNumberSum = 0;
- string[] fileList = System.IO.Directory.GetFileSystemEntries(_path);
- foreach (string item in fileList)
- {
- if (!item.Contains(".meta"))
- _folderNumberSum++;
- }
- return _folderNumberSum;
- }
-
-
- void Update()
- {
- //帧数据无效,禁用脚本
- if (frames == null || frames.Length == 0 || !IsPlay) return;
- //帧率有效 控制帧率
- if (framerate != 0)
- {
- //获取当前时间
- float time = ignoreTimeScale ? Time.unscaledTime : Time.time;
- //计算帧间隔时间
- float interval = Mathf.Abs(1.0f / framerate);
- //满足更新条件,执行更新操作
- if (time - timer > interval)
- {
- //执行更新操作
- DoUpdate();
- }
- }
- else Debug.LogError("帧率必须大于0");
- }
-
- //具体更新操作
- private void DoUpdate()
- {
- //是否打开循环
- if (!loop)
- {
- FrameIndex = Mathf.Clamp(FrameIndex, 0, frames.Length - 1);
- //正向播放判断||倒放判断
- if ((!IsRewind && FrameIndex == (frames.Length - 1)) || (IsRewind && FrameIndex == 0))
- {
- IsPlay = false;
- return;
- }
- }
- //帧数赋值,更新图片
- if (image != null) image.sprite = frames[FrameIndex];
- if (spriteRenderer != null) spriteRenderer.sprite = frames[FrameIndex];
- if (!IsRewind) FrameIndex++;//正向播放索引自增
- else
- {
- FrameIndex--;//倒放,索引自减
- if (FrameIndex < 0) FrameIndex = 0;
- }
- FrameIndex = FrameIndex % frames.Length;
- //设置计时器为当前时间
- timer = ignoreTimeScale ? Time.unscaledTime : Time.time;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。