欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-11-21 12:23:52
序列帧动画经常用到,最直接的方式就是用animation录制。但某些情况下这种方式并不是太友好,需要靠代码的方式进行序列帧动画的实现。 代码实现序列帧动画,基本的思路是定...

序列帧动画经常用到,最直接的方式就是用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;
 }
}

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