Files
MRArcadia/Assets/_Arcadia/Scripts/Plugins/AnimationUtility.cs
2026-03-24 11:25:32 +08:00

439 lines
14 KiB
C#

using System;
using UnityEngine;
using UnityEngine.UI;
using Spine.Unity;
namespace Common
{
/// <summary>
/// 动画播放模式
/// </summary>
public enum LoopMode
{
Once, // 播放一次
Loop, // 循环播放
PingPong // 来回播放
}
/// <summary>
/// 序列图片动画组件
/// </summary>
public class SpriteAnimation
{
private Image _targetImage;
private Sprite[] _frames;
private int _fps;
private LoopMode _loopMode;
private int _currentFrame;
private float _timer;
private bool _isPlaying;
private bool _isPaused;
private bool _isReversing; // 用于PingPong模式
public Action OnComplete { get; set; }
public bool IsPlaying => _isPlaying && !_isPaused;
public int CurrentFrameIndex => _currentFrame;
public int TotalFrames => _frames != null ? _frames.Length : 0;
public SpriteAnimation(Image target, Sprite[] frames, int fps, LoopMode loopMode)
{
_targetImage = target;
_frames = frames;
_fps = Mathf.Max(1, fps);
_loopMode = loopMode;
_currentFrame = 0;
_isReversing = false;
if (_frames != null && _frames.Length > 0)
{
_targetImage.sprite = _frames[0];
}
}
public void Play()
{
if (_frames == null || _frames.Length == 0) return;
_isPlaying = true;
_isPaused = false;
_timer = 0f;
_currentFrame = 0;
_isReversing = false;
if (_frames.Length > 0)
{
_targetImage.sprite = _frames[0];
}
}
public void Pause()
{
_isPaused = true;
}
public void Resume()
{
_isPaused = false;
}
public void Stop()
{
_isPlaying = false;
_isPaused = false;
_currentFrame = 0;
_isReversing = false;
if (_frames != null && _frames.Length > 0)
{
_targetImage.sprite = _frames[0];
}
}
public void Update(float deltaTime)
{
if (!_isPlaying || _isPaused || _frames == null || _frames.Length == 0) return;
_timer += deltaTime;
float frameTime = 1f / _fps;
if (_timer >= frameTime)
{
_timer -= frameTime;
AdvanceFrame();
}
}
private void AdvanceFrame()
{
int frameCount = _frames.Length;
if (_loopMode == LoopMode.Once)
{
_currentFrame++;
if (_currentFrame >= frameCount)
{
_currentFrame = frameCount - 1;
_isPlaying = false;
OnComplete?.Invoke();
}
}
else if (_loopMode == LoopMode.Loop)
{
_currentFrame = (_currentFrame + 1) % frameCount;
}
else if (_loopMode == LoopMode.PingPong)
{
if (!_isReversing)
{
_currentFrame++;
if (_currentFrame >= frameCount - 1)
{
_isReversing = true;
}
}
else
{
_currentFrame--;
if (_currentFrame <= 0)
{
_isReversing = false;
_currentFrame = 0;
OnComplete?.Invoke();
}
}
}
_targetImage.sprite = _frames[_currentFrame];
}
}
/// <summary>
/// Spine动画组件
/// </summary>
public class SpineAnimation
{
private SkeletonAnimation _skeletonAnimation;
private SkeletonGraphic _skeletonGraphic;
private bool _isUI; // true为SkeletonGraphic(UI), false为SkeletonAnimation(3D)
public Action OnComplete { get; set; }
public Action<string> OnEvent { get; set; }
public bool IsPlaying => _skeletonAnimation?.AnimationState?.GetCurrent(0) != null ||
_skeletonGraphic?.AnimationState?.GetCurrent(0) != null;
public SpineAnimation(SkeletonAnimation skeletonAnim, string animationName, bool loop)
{
_skeletonAnimation = skeletonAnim;
_skeletonGraphic = null;
_isUI = false;
PlayAnimation(animationName, loop);
}
public SpineAnimation(SkeletonGraphic skeletonGraphic, string animationName, bool loop)
{
_skeletonAnimation = null;
_skeletonGraphic = skeletonGraphic;
_isUI = true;
PlayAnimation(animationName, loop);
}
private void PlayAnimation(string animationName, bool loop)
{
if (_isUI && _skeletonGraphic != null)
{
_skeletonGraphic.AnimationState.Start -= OnAnimationStart;
_skeletonGraphic.AnimationState.End -= OnAnimationEnd;
_skeletonGraphic.AnimationState.Event -= OnAnimationEvent;
_skeletonGraphic.AnimationState.Start += OnAnimationStart;
_skeletonGraphic.AnimationState.End += OnAnimationEnd;
_skeletonGraphic.AnimationState.Event += OnAnimationEvent;
_skeletonGraphic.AnimationState.SetAnimation(0, animationName, loop);
}
else if (_skeletonAnimation != null)
{
_skeletonAnimation.AnimationState.Start -= OnAnimationStart;
_skeletonAnimation.AnimationState.End -= OnAnimationEnd;
_skeletonAnimation.AnimationState.Event -= OnAnimationEvent;
_skeletonAnimation.AnimationState.Start += OnAnimationStart;
_skeletonAnimation.AnimationState.End += OnAnimationEnd;
_skeletonAnimation.AnimationState.Event += OnAnimationEvent;
_skeletonAnimation.AnimationState.SetAnimation(0, animationName, loop);
}
}
private void OnAnimationStart(Spine.TrackEntry trackEntry)
{
// 动画开始
}
private void OnAnimationEnd(Spine.TrackEntry trackEntry)
{
OnComplete?.Invoke();
}
private void OnAnimationEvent(Spine.TrackEntry trackEntry, Spine.Event e)
{
OnEvent?.Invoke(e.Data.Name);
}
/// <summary>
/// 切换动画
/// </summary>
public void SetAnimation(string animationName, bool loop)
{
if (_isUI && _skeletonGraphic != null)
{
_skeletonGraphic.AnimationState.SetAnimation(0, animationName, loop);
}
else if (_skeletonAnimation != null)
{
_skeletonAnimation.AnimationState.SetAnimation(0, animationName, loop);
}
}
/// <summary>
/// 添加混合动画
/// </summary>
public void AddAnimation(string animationName, bool loop, float delay = 0f)
{
if (_isUI && _skeletonGraphic != null)
{
_skeletonGraphic.AnimationState.AddAnimation(0, animationName, loop, delay);
}
else if (_skeletonAnimation != null)
{
_skeletonAnimation.AnimationState.AddAnimation(0, animationName, loop, delay);
}
}
/// <summary>
/// 清除所有动画
/// </summary>
public void Clear()
{
if (_isUI && _skeletonGraphic != null)
{
_skeletonGraphic.AnimationState.ClearTracks();
}
else if (_skeletonAnimation != null)
{
_skeletonAnimation.AnimationState.ClearTracks();
}
}
/// <summary>
/// 暂停动画
/// </summary>
public void Pause()
{
if (_isUI && _skeletonGraphic != null)
{
_skeletonGraphic.timeScale = 0f;
}
else if (_skeletonAnimation != null)
{
_skeletonAnimation.timeScale = 0f;
}
}
/// <summary>
/// 恢复动画
/// </summary>
public void Resume()
{
if (_isUI && _skeletonGraphic != null)
{
_skeletonGraphic.timeScale = 1f;
}
else if (_skeletonAnimation != null)
{
_skeletonAnimation.timeScale = 1f;
}
}
}
/// <summary>
/// 动画工具类
/// </summary>
public static class AnimationUtility
{
/// <summary>
/// 播放序列图片动画
/// </summary>
/// <param name="target">目标Image组件</param>
/// <param name="frames">Sprite数组</param>
/// <param name="fps">帧率</param>
/// <param name="loopMode">循环模式</param>
/// <returns>序列图片动画对象</returns>
public static SpriteAnimation PlaySpriteAnimation(Image target, Sprite[] frames, int fps = 12, LoopMode loopMode = LoopMode.Loop)
{
if (target == null)
{
Debug.LogError("AnimationUtility: Target Image is null!");
return null;
}
if (frames == null || frames.Length == 0)
{
Debug.LogError("AnimationUtility: Frames array is null or empty!");
return null;
}
SpriteAnimation anim = new SpriteAnimation(target, frames, fps, loopMode);
anim.Play();
// 添加到更新列表
SpriteAnimationManager.Add(anim);
return anim;
}
/// <summary>
/// 播放Spine动画 (SkeletonAnimation - 3D/世界空间)
/// </summary>
/// <param name="skeletonAnim">SkeletonAnimation组件</param>
/// <param name="animationName">动画名称</param>
/// <param name="loop">是否循环</param>
/// <returns>Spine动画对象</returns>
public static SpineAnimation PlaySpineAnimation(SkeletonAnimation skeletonAnim, string animationName, bool loop = true)
{
if (skeletonAnim == null)
{
Debug.LogError("AnimationUtility: SkeletonAnimation is null!");
return null;
}
return new SpineAnimation(skeletonAnim, animationName, loop);
}
/// <summary>
/// 播放Spine动画 (SkeletonGraphic - UI)
/// </summary>
/// <param name="skeletonGraphic">SkeletonGraphic组件</param>
/// <param name="animationName">动画名称</param>
/// <param name="loop">是否循环</param>
/// <returns>Spine动画对象</returns>
public static SpineAnimation PlaySpineAnimation(SkeletonGraphic skeletonGraphic, string animationName, bool loop = true)
{
if (skeletonGraphic == null)
{
Debug.LogError("AnimationUtility: SkeletonGraphic is null!");
return null;
}
return new SpineAnimation(skeletonGraphic, animationName, loop);
}
}
/// <summary>
/// 序列图片动画管理器 - 用于在Update中更新动画
/// </summary>
public class SpriteAnimationManager : MonoBehaviour
{
private static SpriteAnimationManager _instance;
private static SpriteAnimationManager Instance
{
get
{
if (_instance == null)
{
GameObject go = new GameObject("SpriteAnimationManager");
_instance = go.AddComponent<SpriteAnimationManager>();
DontDestroyOnLoad(go);
}
return _instance;
}
}
private System.Collections.Generic.List<SpriteAnimation> _animations = new System.Collections.Generic.List<SpriteAnimation>();
public static void Add(SpriteAnimation anim)
{
if (anim == null) return;
if (!Instance._animations.Contains(anim))
{
Instance._animations.Add(anim);
}
}
public static void Remove(SpriteAnimation anim)
{
if (anim == null) return;
Instance._animations.Remove(anim);
}
private void Update()
{
float deltaTime = Time.deltaTime;
// 倒序遍历以便安全删除
for (int i = _animations.Count - 1; i >= 0; i--)
{
if (i < _animations.Count)
{
SpriteAnimation anim = _animations[i];
if (anim != null && anim.IsPlaying)
{
anim.Update(deltaTime);
}
else if (anim != null && !anim.IsPlaying)
{
// 动画停止后从列表中移除
_animations.RemoveAt(i);
}
}
}
}
}
}