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;
	}
}
  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Unity DOTS(Data-Oriented Technology Stack)中,序列帧动画可以使用ECS(Entity Component System)和Burst Compiler来提高游戏性能。以下是基本步骤: 1. 创建一个Animation组件,用于存储动画帧的信息。 ``` [Serializable] public struct Animation : IComponentData { public int frameCount; public float frameTime; public float elapsedTime; } ``` 2. 创建一个AnimationSystem系统,用于更新动画帧的信息。 ``` public class AnimationSystem : SystemBase { protected override void OnUpdate() { float deltaTime = Time.DeltaTime; Entities.ForEach((ref Animation animation) => { animation.elapsedTime += deltaTime; int frameIndex = (int)(animation.elapsedTime / animation.frameTime) % animation.frameCount; // 更新动画帧 }).ScheduleParallel(); } } ``` 3. 创建一个RenderMesh组件,用于显示动画帧。 ``` public struct RenderMesh : IComponentData { public Mesh mesh; public Material material; public int subMesh; } ``` 4. 创建一个RenderSystem系统,用于显示动画帧。 ``` public class RenderSystem : SystemBase { private EndSimulationEntityCommandBufferSystem _entityCommandBufferSystem; protected override void OnCreate() { _entityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>(); } protected override void OnUpdate() { EntityCommandBuffer commandBuffer = _entityCommandBufferSystem.CreateCommandBuffer(); Entities.ForEach((Entity entity, ref RenderMesh renderMesh, in Animation animation) => { // 更新RenderMesh的Mesh和Material commandBuffer.SetSharedComponent(entity, new RenderMesh { mesh = ..., // 根据动画帧更新Mesh material = renderMesh.material, subMesh = renderMesh.subMesh }); }).ScheduleParallel(); } } ``` 5. 在场景中创建一个实体,并添加Animation和RenderMesh组件。 ``` Entity entity = EntityManager.CreateEntity(); EntityManager.AddComponentData(entity, new Animation { frameCount = ..., // 动画帧数 frameTime = ..., // 动画帧时间间隔 elapsedTime = 0f }); EntityManager.AddSharedComponentData(entity, new RenderMesh { mesh = ..., // 初始Mesh material = ..., // 初始Material subMesh = 0 }); ``` 这样,序列帧动画就可以在Unity DOTS中实现了。注意,这只是一个基本示例,具体实现可能会因游戏需求而有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值