Unity——用代码实现序列帧动画

序列帧动画经常用到,最直接的方式就是用Animation录制。但某些情况下这种方式并不是太友好,需要靠代码的方式进行序列帧动画的实现。

代码实现序列帧动画,基本的思路是定义一个序列帧的数组/列表,根据时间的流逝来确定使用哪一帧并更新显示。

NGUI的UI2DSpriteAnimation已经实现了此功能,但是它支持的目标只有Native2D的SpriteRenderer组件或者NGUI自身的UI2DSprite组件,并不支持UGUI的Image组件。

当然可以通过改写源码的方式来添加对Image组件的支持,不过秉着学习的目的,我这里重新写了一个针对Image组件的序列帧动画播放器。

代码如下,注释写的很详细了,不再赘述。

using UnityEngine;
using UnityEngine.UI;

[RequireComponent (typeof(Image))]
public class UIFrameAnimator : MonoBehaviour
{
    /// <summary>
    /// 序列帧
    /// </summary>
    public Sprite[] Frames{ get { return frames; } set { frames = value; } }

    [SerializeField]private Sprite[] frames = null;

    /// <summary>
    /// 帧率,为正时正向播放,为负时反向播放
    /// </summary>
    public int Framerate { get { return framerate; } set { framerate = value; } }

    [SerializeField]private int framerate = 20;

    /// <summary>
    /// 是否忽略timeScale
    /// </summary>
    public bool IgnoreTimeScale{ get { return ignoreTimeScale; } set { ignoreTimeScale = value; } }

    [SerializeField]private bool ignoreTimeScale = true;

    //是否循环
    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 int CurrentFrameIndex{ get { return currentFrameIndex; } }

    private int currentFrameIndex = 0;
    //下一次更新时间
    private float timer = 0.0f;
    //目标Image组件
    private Image image;

    /// <summary>
    /// 从停止的位置播放动画
    /// </summary>
    public void Play ()
    {
        //帧数据有效
        if (frames != null && frames.Length > 0) {
            //脚本被禁用并且是非循环模式
            if (this.enabled == false && loop == false) {
                //计算下一帧索引
                int newIndex = framerate > 0 ? currentFrameIndex + 1 : currentFrameIndex - 1;
                if (newIndex < 0 || newIndex >= frames.Length)
                    currentFrameIndex = framerate < 0 ? frames.Length - 1 : 0;
            }
            //启用脚本
            this.enabled = true;
            //执行更新操作
            DoUpdate ();
        }
    }

    /// <summary>
    /// 暂停动画
    /// </summary>
    public void Pause ()
    {
        this.enabled = false;
    }

    /// <summary>
    /// 重设动画
    /// </summary>
    public void ResetToBeginning ()
    {
        currentFrameIndex = framerate < 0 ? frames.Length - 1 : 0;
        DoUpdate ();
    }

    //自动开启动画
    void Start ()
    {
        Play ();
    }

    void Update ()
    {
        //帧数据无效,禁用脚本
        if (frames == null || frames.Length == 0) {
            this.enabled = false;
        } else if (framerate != 0) {//帧率有效
            //获取当前时间
            float time = ignoreTimeScale ? Time.unscaledTime : Time.time;
            //获取曲线值
            float curveValue = curve.Evaluate ((float)currentFrameIndex / framerate);
            curveValue = curveValue == 0 ? 0.01f : curveValue;
            float curvedFramerate = curveValue * framerate;
            //计算帧间隔时间
            float interval = Mathf.Abs (1.0f / curvedFramerate);

            //满足更新条件,执行更新操作
            if (time - timer > interval) {
                //执行更新操作
                DoUpdate ();
            }
        }
    }

    //具体更新操作
    private void DoUpdate ()
    {
        //计算新的索引
        int nextIndex = currentFrameIndex + (int)Mathf.Sign (framerate);
        //非循环模式并且索引越界,表示动画已播放完成,禁用脚本
        if (loop == false && (nextIndex < 0 || nextIndex >= frames.Length)) {
            enabled = false;
            return;
        }
        //将新的索引定位在有效范围内
        while (nextIndex < 0) {
            nextIndex += frames.Length;
        }
        while (nextIndex >= frames.Length) {
            nextIndex -= frames.Length;
        }
        //使用计算出的索引
        currentFrameIndex = nextIndex;

        //更新图片
        image = image ?? this.GetComponent<Image> ();
        image.sprite = frames [currentFrameIndex];

        //设置计时器为当前时间
        timer = ignoreTimeScale ? Time.unscaledTime : Time.time;
    }
}

猜你喜欢

转载自blog.csdn.net/SerenaHaven/article/details/79273114
今日推荐