439 lines
14 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |