DCS/ruiyiweiUX/Assets/GeneralTools/Scripts/Always/Frames/FramesAnimator.cs

305 lines
9.7 KiB
C#
Raw Permalink Normal View History

2026-06-09 13:59:11 +08:00
using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace GeneralTools
{
/// <summary>
/// 序列帧动画播放器
/// 支持UGUI的Image和Unity2D的SpriteRenderer
/// </summary>
public class FramesAnimator : MonoBehaviour
{
//目标Image组件
private RawImage image;
private List<FramesAttribute> framesList;
/// <summary>
/// 全部结束事件
/// </summary>
public event Action OnAllFinishEvent;
//是否允许播放
private bool isAllowPlay = true;
//当前帧索引
private int currentFrameIndex = 0;
//当前动画序号索引
private int currentAnimationIndex = 0;
//下一次更新时间
private float timer = 0.0f;
public FramesAnimator Init(List<FramesAttribute> frameAttritubes)
{
Stop();
image = GetComponent<RawImage>();
if (image == null)
{
Debug.LogError("未找到RawImage", gameObject);
}
framesList = frameAttritubes;
OnAllFinishEvent = null;
return this;
}
public FramesAnimator Init(params List<Texture>[] texArr)
{
List<FramesAttribute> temp = new List<FramesAttribute>();
for (int i = 0; i < texArr.Length; i++)
{
FramesAttribute newFM = new FramesAttribute(texArr[i], i == texArr.Length - 1);
temp.Add(newFM);
}
return Init(temp);
}
public FramesAnimator Init(List<List<Texture>> texList, List<bool> loopList, List<Action> finishEventList = null)
{
List<FramesAttribute> temp = new List<FramesAttribute>();
if (finishEventList == null)
finishEventList = new List<Action>();
for (int i = 0; i < texList.Count; i++)
{
bool il = i < loopList.Count ? loopList[i] : false;
FramesAttribute newFM = new FramesAttribute(texList[i], il);
newFM.OnFinishEvent += i < finishEventList.Count ? finishEventList[i] : null;
temp.Add(newFM);
}
return Init(temp);
}
public void Play()
{
isAllowPlay = true;
gameObject.SetActive(true);
}
public void Pause()
{
isAllowPlay = false;
}
public void Stop()
{
isAllowPlay = false;
ResetIndex();
}
public void Hide()
{
Stop();
gameObject.SetActive(false);
}
public void RePlay()
{
isAllowPlay = false;
ResetIndex();
if (framesList.Count > 0)
{
if (framesList[0].framesList.Count > 0)
{
image.texture = framesList[0].framesList[currentFrameIndex];
}
}
Play();
}
public void PlayIndex(int index)
{
if (framesList == null)
return;
if (index < 0 || index >= framesList.Count)
return;
isAllowPlay = false;
currentAnimationIndex = index;
currentFrameIndex = framesList[currentAnimationIndex].framerate > 0 ? 0 : framesList[currentAnimationIndex].framesList.Count - 1;
if (framesList[currentAnimationIndex].framesList.Count > 0)
{
image.texture = framesList[currentAnimationIndex].framesList[currentFrameIndex];
}
Play();
}
/// <summary>
/// 改变一个索引的临时loop
/// </summary>
public void SetLoopOfIndex(int index, bool loop)
{
if (index >= 0 && index < framesList.Count)
{
framesList[index].loop = loop;
}
}
/// <summary>
/// 改变一个索引的永久loop
/// </summary>
public void SetLoopOfIndexForever(int index, bool loop)
{
if (index >= 0 && index < framesList.Count)
{
framesList[index].InitialLoop = loop;
}
}
/// <summary>
/// 获得当前动画索引
/// </summary>
public int GetCurrentAnimationIndex()
{
return currentAnimationIndex;
}
/// <summary>
/// 改变当前索引的临时loop
/// </summary>
public void SetCurrentAnimationLoop(bool loop)
{
SetLoopOfIndex(currentAnimationIndex, loop);
}
/// <summary>
/// 动画全部设置为不循环,播放完销毁自身
/// </summary>
public void GoDestroy()
{
for (int i = 0; i < framesList.Count; i++)
{
SetLoopOfIndexForever(i, false);
}
OnAllFinishEvent += () =>
{
Destroy(gameObject);
};
}
/// <summary>
/// 重设动画索引
/// </summary>
private int ResetIndex()
{
currentAnimationIndex = 0;
//重置Loop
if (framesList != null)
{
for (int i = 0; i < framesList.Count; i++)
{
framesList[i].ReSetLoop();
}
currentFrameIndex = framesList[currentAnimationIndex].framerate > 0 ? 0 : framesList[currentAnimationIndex].framesList.Count - 1;
}
return currentFrameIndex;
}
void FixedUpdate()
{
if (framesList == null || framesList.Count == 0 || image == null)
{
return;
}
else
{
//是否允许播放
if (!isAllowPlay) return;
PlayOneFrame(framesList[currentAnimationIndex]);
}
}
private void PlayOneFrame(FramesAttribute fm)
{
if (fm == null || fm.framesList.Count == 0 || fm.framerate == 0)
{
return;
}
//获取当前时间
float time = fm.ignoreTimeScale ? Time.unscaledTime : Time.time;
//计算帧间隔时间
float interval = Mathf.Abs(1.0f / fm.framerate);
//满足更新条件,执行更新操作
if (time - timer > interval)
{
//执行更新操作
DoUpdate(fm);
}
}
//具体更新操作
private void DoUpdate(FramesAttribute fm)
{
//计算新的索引
int nextIndex = currentFrameIndex + (fm.framerate > 0 ? 1 : -1);
//索引越界,表示已经到结束帧
if (nextIndex < 0 || nextIndex >= fm.framesList.Count)
{
nextIndex = 0;
//不循环,整体动画索引+1
if (fm.loop == false)
{
//播放结束事件
fm.OnFinish();
currentAnimationIndex += 1;
if (currentAnimationIndex >= framesList.Count)
{
//全部播放完毕,帧索引不归零
isAllowPlay = false;
OnAllFinishEvent?.Invoke();
currentAnimationIndex = framesList.Count - 1;
nextIndex = fm.framesList.Count - 1;
}
else
{
//未结束,指向下一个动画,帧索引归零
fm = framesList[currentAnimationIndex];
nextIndex = fm.framerate > 0 ? 0 : fm.framesList.Count - 1;
}
}
else
{
//循环,帧索引归零
nextIndex = fm.framerate > 0 ? 0 : fm.framesList.Count - 1;
}
}
//钳制索引
currentFrameIndex = Mathf.Clamp(nextIndex, 0, fm.framesList.Count - 1);
//更新图片
RefreshTexture(fm, currentFrameIndex);
//设置计时器为当前时间
timer = fm.ignoreTimeScale ? Time.unscaledTime : Time.time;
}
private void RefreshTexture(FramesAttribute fm, int index)
{
image.texture = fm.framesList[index];
}
public void ManualUpdatePositive()
{
FramesAttribute fm = framesList[currentAnimationIndex];
DoUpdate(fm);
}
//反向手动刷新
public void ManualUpdateReverse()
{
FramesAttribute fm = framesList[currentAnimationIndex];
fm.framerate *= -1;
DoUpdate(fm);
fm.framerate *= -1;
}
}
public class FramesAttribute
{
public List<Texture> framesList;
public bool loop;
public bool InitialLoop { get { return initialLoop; } set { loop = value; initialLoop = value; } }
private bool initialLoop;
public event Action OnFinishEvent;
public float framerate = 24.0f;
public bool ignoreTimeScale = true;
public FramesAttribute()
{
}
public FramesAttribute(List<Texture> _framesList, bool _loop)
{
framesList = _framesList;
InitialLoop = _loop;
OnFinishEvent = null;
}
public void ReSetLoop()
{
InitialLoop = InitialLoop;
}
public void OnFinish()
{
OnFinishEvent?.Invoke();
}
}
}