Unity代码实现序列帧动画播放器

 更新时间:2019年02月22日 15:07:51   作者:serenahaven  
这篇文章主要为大家详细介绍了Unity代码实现序列帧动画播放器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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

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

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

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

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

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;
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • C#中Action和Func的区别

    C#中Action和Func的区别

    这篇文章主要介绍了C#中Action和Func的区别,是进行C#程序设计时需要加以注意的知识点,需要的朋友可以参考下
    2014-09-09
  • C#将PDF文档转换为Markdown文档的代码实现

    C#将PDF文档转换为Markdown文档的代码实现

    将PDF文件转换为Markdown格式是一个非常实用的需求,尤其是在需要将内容从固定布局的PDF文件中提取出来,并转换为更易于编辑和处理的文本格式时,本文将介绍如何通过C#代码将PDF文档转换Markdown(MD)文档,需要的朋友可以参考下
    2024-10-10
  • 使用C#校验画布签名图片是否为空白

    使用C#校验画布签名图片是否为空白

    这篇文章主要为大家详细介绍了如何使用C#校验画布签名图片是否为空白,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-11-11
  • C#中验证sql语句是否正确(不执行语句)

    C#中验证sql语句是否正确(不执行语句)

    C#中验证sql语句是否正确(不执行语句),需要的朋友可以参考一下
    2013-03-03
  • WCF实现双向通信

    WCF实现双向通信

    这篇文章介绍了WCF实现双向通信的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • C#获取打印机列表方法介绍

    C#获取打印机列表方法介绍

    这篇文章介绍了C#获取打印机列表的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • DevExpress实现GridControl根据列选中一行

    DevExpress实现GridControl根据列选中一行

    这篇文章主要介绍了DevExpress实现GridControl根据列选中一行,比较实用的功能,需要的朋友可以参考下
    2014-08-08
  • C# 图片剪切与缩小的实例

    C# 图片剪切与缩小的实例

    C# 剪切图片与缩小的实例,需要的朋友可以参考一下
    2013-04-04
  • C# 6.0 的知识梳理

    C# 6.0 的知识梳理

    目前最新的版本是C# 7.0,VS的最新版本为Visual Studio 2017 RC,两者都尚未进入正式阶段。C# 6.0虽说出了一段时间,但是似乎有许多人对这一块知识并不了解。本文将对C# 6.0的知识进行梳理,下面跟着小编一起来看下吧
    2016-12-12
  • C#使用浏览按钮获得文件路径和文件夹路径的方法

    C#使用浏览按钮获得文件路径和文件夹路径的方法

    这篇文章主要介绍了C#使用浏览按钮获得文件路径和文件夹路径的方法,结合实例形式分析了C#浏览器事件响应及文件操作相关技巧,需要的朋友可以参考下
    2017-05-05

最新评论