|
|
@@ -0,0 +1,1094 @@
|
|
|
+单序列帧动画 & Example示例
|
|
|
+
|
|
|
+## SequenceFramePlayer
|
|
|
+
|
|
|
+
|
|
|
+``` cs
|
|
|
+using System.Collections;
|
|
|
+using System.Collections.Generic;
|
|
|
+using UnityEngine;
|
|
|
+using UnityEngine.UI;
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// 动画序列数据结构
|
|
|
+/// </summary>
|
|
|
+[System.Serializable]
|
|
|
+public class AnimationSequence
|
|
|
+{
|
|
|
+ [Header("序列设置")]
|
|
|
+ public string name = "Animation"; // 动画名称
|
|
|
+ public Sprite[] frames; // 序列帧图片数组
|
|
|
+ [Range(1f, 120f)]
|
|
|
+ public float framerate = 30f; // 播放帧率
|
|
|
+ public bool loop = true; // 是否循环播放
|
|
|
+
|
|
|
+ [Header("渲染组件")]
|
|
|
+ public SpriteRenderer targetSpriteRenderer; // 目标SpriteRenderer组件
|
|
|
+ public Image targetImage; // 目标UI Image组件
|
|
|
+ public bool useGlobalRenderer = true; // 是否使用全局渲染器(如果为false,使用上面指定的组件)
|
|
|
+
|
|
|
+ [Header("渲染设置")]
|
|
|
+ public bool setNativeSize = false; // 是否设置为原始尺寸(仅对Image有效)
|
|
|
+ public bool preserveAspect = true; // 是否保持宽高比(仅对Image有效)
|
|
|
+ public Color tintColor = Color.white; // 着色颜色
|
|
|
+ public Material overrideMaterial; // 覆盖材质
|
|
|
+
|
|
|
+ [Header("变换设置")]
|
|
|
+ public bool useCustomScale = false; // 是否使用自定义缩放
|
|
|
+ public Vector3 customScale = Vector3.one; // 自定义缩放值
|
|
|
+ public bool useCustomRotation = false; // 是否使用自定义旋转
|
|
|
+ public Vector3 customRotation = Vector3.zero; // 自定义旋转值
|
|
|
+
|
|
|
+ [Header("排序设置")]
|
|
|
+ public bool overrideSortingOrder = false; // 是否覆盖排序层级
|
|
|
+ public int sortingOrder = 0; // 排序层级
|
|
|
+ public string sortingLayerName = "Default"; // 排序层名称
|
|
|
+
|
|
|
+ [Header("曲线控制")]
|
|
|
+ public AnimationCurve curve; // 动画曲线,用于控制播放速度
|
|
|
+ public bool useCurve = false; // 是否使用曲线控制
|
|
|
+
|
|
|
+ // 运行时数据(不序列化)
|
|
|
+ [System.NonSerialized]
|
|
|
+ private Vector3 originalScale;
|
|
|
+ [System.NonSerialized]
|
|
|
+ private Vector3 originalRotation;
|
|
|
+ [System.NonSerialized]
|
|
|
+ private Color originalColor;
|
|
|
+ [System.NonSerialized]
|
|
|
+ private Material originalMaterial;
|
|
|
+ [System.NonSerialized]
|
|
|
+ private int originalSortingOrder;
|
|
|
+ [System.NonSerialized]
|
|
|
+ private string originalSortingLayer;
|
|
|
+ [System.NonSerialized]
|
|
|
+ private bool hasStoredOriginalValues = false;
|
|
|
+
|
|
|
+ // 构造函数
|
|
|
+ public AnimationSequence()
|
|
|
+ {
|
|
|
+ curve = AnimationCurve.Linear(0f, 1f, 1f, 1f);
|
|
|
+ }
|
|
|
+
|
|
|
+ public AnimationSequence(string animName, Sprite[] animFrames, float rate = 30f, bool isLoop = true)
|
|
|
+ {
|
|
|
+ name = animName;
|
|
|
+ frames = animFrames;
|
|
|
+ framerate = rate;
|
|
|
+ loop = isLoop;
|
|
|
+ curve = AnimationCurve.Linear(0f, 1f, 1f, 1f);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 存储原始值(用于恢复)
|
|
|
+ /// </summary>
|
|
|
+ public void StoreOriginalValues(SpriteRenderer spriteRenderer, Image image)
|
|
|
+ {
|
|
|
+ if (hasStoredOriginalValues) return;
|
|
|
+
|
|
|
+ if (!useGlobalRenderer)
|
|
|
+ {
|
|
|
+ if (targetSpriteRenderer != null)
|
|
|
+ spriteRenderer = targetSpriteRenderer;
|
|
|
+ if (targetImage != null)
|
|
|
+ image = targetImage;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (spriteRenderer != null)
|
|
|
+ {
|
|
|
+ originalScale = spriteRenderer.transform.localScale;
|
|
|
+ originalRotation = spriteRenderer.transform.localEulerAngles;
|
|
|
+ originalColor = spriteRenderer.color;
|
|
|
+ originalMaterial = spriteRenderer.material;
|
|
|
+ originalSortingOrder = spriteRenderer.sortingOrder;
|
|
|
+ originalSortingLayer = spriteRenderer.sortingLayerName;
|
|
|
+ }
|
|
|
+ else if (image != null)
|
|
|
+ {
|
|
|
+ originalScale = image.transform.localScale;
|
|
|
+ originalRotation = image.transform.localEulerAngles;
|
|
|
+ originalColor = image.color;
|
|
|
+ originalMaterial = image.material;
|
|
|
+ }
|
|
|
+
|
|
|
+ hasStoredOriginalValues = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 恢复原始值
|
|
|
+ /// </summary>
|
|
|
+ public void RestoreOriginalValues(SpriteRenderer spriteRenderer, Image image)
|
|
|
+ {
|
|
|
+ if (!hasStoredOriginalValues) return;
|
|
|
+
|
|
|
+ if (!useGlobalRenderer)
|
|
|
+ {
|
|
|
+ if (targetSpriteRenderer != null)
|
|
|
+ spriteRenderer = targetSpriteRenderer;
|
|
|
+ if (targetImage != null)
|
|
|
+ image = targetImage;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (spriteRenderer != null)
|
|
|
+ {
|
|
|
+ spriteRenderer.transform.localScale = originalScale;
|
|
|
+ spriteRenderer.transform.localEulerAngles = originalRotation;
|
|
|
+ spriteRenderer.color = originalColor;
|
|
|
+ spriteRenderer.material = originalMaterial;
|
|
|
+ spriteRenderer.sortingOrder = originalSortingOrder;
|
|
|
+ spriteRenderer.sortingLayerName = originalSortingLayer;
|
|
|
+ }
|
|
|
+ else if (image != null)
|
|
|
+ {
|
|
|
+ image.transform.localScale = originalScale;
|
|
|
+ image.transform.localEulerAngles = originalRotation;
|
|
|
+ image.color = originalColor;
|
|
|
+ image.material = originalMaterial;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 应用渲染设置
|
|
|
+ /// </summary>
|
|
|
+ public void ApplyRenderSettings(SpriteRenderer spriteRenderer, Image image)
|
|
|
+ {
|
|
|
+ if (!useGlobalRenderer)
|
|
|
+ {
|
|
|
+ if (targetSpriteRenderer != null)
|
|
|
+ spriteRenderer = targetSpriteRenderer;
|
|
|
+ if (targetImage != null)
|
|
|
+ image = targetImage;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (spriteRenderer != null)
|
|
|
+ {
|
|
|
+ // 应用颜色
|
|
|
+ spriteRenderer.color = tintColor;
|
|
|
+
|
|
|
+ // 应用材质
|
|
|
+ if (overrideMaterial != null)
|
|
|
+ spriteRenderer.material = overrideMaterial;
|
|
|
+
|
|
|
+ // 应用排序设置
|
|
|
+ if (overrideSortingOrder)
|
|
|
+ {
|
|
|
+ spriteRenderer.sortingOrder = sortingOrder;
|
|
|
+ spriteRenderer.sortingLayerName = sortingLayerName;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 应用变换设置
|
|
|
+ if (useCustomScale)
|
|
|
+ spriteRenderer.transform.localScale = customScale;
|
|
|
+ if (useCustomRotation)
|
|
|
+ spriteRenderer.transform.localEulerAngles = customRotation;
|
|
|
+ }
|
|
|
+ else if (image != null)
|
|
|
+ {
|
|
|
+ // 应用颜色
|
|
|
+ image.color = tintColor;
|
|
|
+
|
|
|
+ // 应用材质
|
|
|
+ if (overrideMaterial != null)
|
|
|
+ image.material = overrideMaterial;
|
|
|
+
|
|
|
+ // 应用Image特有设置
|
|
|
+ if (setNativeSize)
|
|
|
+ image.SetNativeSize();
|
|
|
+ image.preserveAspect = preserveAspect;
|
|
|
+
|
|
|
+ // 应用变换设置
|
|
|
+ if (useCustomScale)
|
|
|
+ image.transform.localScale = customScale;
|
|
|
+ if (useCustomRotation)
|
|
|
+ image.transform.localEulerAngles = customRotation;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取实际使用的SpriteRenderer
|
|
|
+ /// </summary>
|
|
|
+ public SpriteRenderer GetSpriteRenderer(SpriteRenderer defaultRenderer)
|
|
|
+ {
|
|
|
+ return useGlobalRenderer ? defaultRenderer : targetSpriteRenderer;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取实际使用的Image
|
|
|
+ /// </summary>
|
|
|
+ public Image GetImage(Image defaultImage)
|
|
|
+ {
|
|
|
+ return useGlobalRenderer ? defaultImage : targetImage;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// 序列帧动画播放器
|
|
|
+/// 支持SpriteRenderer和Image两种组件,支持多个动画序列
|
|
|
+/// </summary>
|
|
|
+public class SequenceFramePlayer : MonoBehaviour
|
|
|
+{
|
|
|
+ [Header("动画序列")]
|
|
|
+ [SerializeField] private List<AnimationSequence> animations = new List<AnimationSequence>(); // 多个动画序列
|
|
|
+ [SerializeField] private int defaultAnimationIndex = 0; // 默认播放的动画索引
|
|
|
+ [SerializeField] private bool playOnStart = true; // 启动时自动播放
|
|
|
+ [SerializeField] private bool ignoreTimeScale = false; // 是否忽略时间缩放
|
|
|
+
|
|
|
+ [Header("渲染组件")]
|
|
|
+ [SerializeField] private SpriteRenderer spriteRenderer; // SpriteRenderer组件
|
|
|
+ [SerializeField] private Image image; // UI Image组件
|
|
|
+
|
|
|
+ // 私有变量
|
|
|
+ private int currentAnimationIndex = 0; // 当前播放的动画索引
|
|
|
+ private int currentFrameIndex = 0;
|
|
|
+ private float timer = 0f;
|
|
|
+ private bool isPlaying = false;
|
|
|
+
|
|
|
+ // 属性
|
|
|
+ public bool IsPlaying => isPlaying;
|
|
|
+ public int CurrentAnimationIndex => currentAnimationIndex;
|
|
|
+ public string CurrentAnimationName => GetCurrentAnimation()?.name ?? "None";
|
|
|
+ public int CurrentFrame => currentFrameIndex;
|
|
|
+ public int TotalFrames => GetCurrentAnimation()?.frames?.Length ?? 0;
|
|
|
+ public float Progress => TotalFrames > 0 ? (float)currentFrameIndex / TotalFrames : 0f;
|
|
|
+ public int AnimationCount => animations?.Count ?? 0;
|
|
|
+
|
|
|
+ // 事件
|
|
|
+ public System.Action OnAnimationStart;
|
|
|
+ public System.Action OnAnimationComplete;
|
|
|
+ public System.Action<int> OnFrameChanged;
|
|
|
+ public System.Action<int, string> OnAnimationChanged; // 动画切换事件 (索引, 名称)
|
|
|
+
|
|
|
+ private void Awake()
|
|
|
+ {
|
|
|
+ // 自动获取组件
|
|
|
+ if (spriteRenderer == null)
|
|
|
+ spriteRenderer = GetComponent<SpriteRenderer>();
|
|
|
+ if (image == null)
|
|
|
+ image = GetComponent<Image>();
|
|
|
+
|
|
|
+ // 初始化默认动画索引
|
|
|
+ if (defaultAnimationIndex >= 0 && defaultAnimationIndex < animations.Count)
|
|
|
+ {
|
|
|
+ currentAnimationIndex = defaultAnimationIndex;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Start()
|
|
|
+ {
|
|
|
+ if (playOnStart && HasValidAnimation())
|
|
|
+ {
|
|
|
+ Play();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取当前动画序列
|
|
|
+ /// </summary>
|
|
|
+ private AnimationSequence GetCurrentAnimation()
|
|
|
+ {
|
|
|
+ if (animations == null || currentAnimationIndex < 0 || currentAnimationIndex >= animations.Count)
|
|
|
+ return null;
|
|
|
+ return animations[currentAnimationIndex];
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查当前是否有有效的动画
|
|
|
+ /// </summary>
|
|
|
+ private bool HasValidAnimation()
|
|
|
+ {
|
|
|
+ var anim = GetCurrentAnimation();
|
|
|
+ return anim != null && anim.frames != null && anim.frames.Length > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Update()
|
|
|
+ {
|
|
|
+ if (!isPlaying || !HasValidAnimation())
|
|
|
+ return;
|
|
|
+
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+
|
|
|
+ // 计算最终帧率
|
|
|
+ float finalFramerate = currentAnim.framerate;
|
|
|
+
|
|
|
+ if (currentAnim.useCurve && currentAnim.curve != null)
|
|
|
+ {
|
|
|
+ // 从曲线获取当前帧率
|
|
|
+ float curveValue = currentAnim.curve.Evaluate((float)currentFrameIndex / currentAnim.frames.Length);
|
|
|
+ finalFramerate = curveValue * currentAnim.framerate;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 帧率有效(添加最小值保护)
|
|
|
+ if (finalFramerate > 0.01f) // 最小帧率保护
|
|
|
+ {
|
|
|
+ // 获取当前时间
|
|
|
+ float time = ignoreTimeScale ? Time.unscaledTime : Time.time;
|
|
|
+ // 计算帧间隔时间
|
|
|
+ float interval = 1.0f / finalFramerate;
|
|
|
+
|
|
|
+ // 满足更新条件,执行更新操作
|
|
|
+ if (time - timer > interval)
|
|
|
+ {
|
|
|
+ // 执行更新操作
|
|
|
+ DoUpdate();
|
|
|
+ timer = time;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // 帧率过低时,使用默认帧率
|
|
|
+ Debug.LogWarning($"Framerate too low ({finalFramerate}), using default framerate.");
|
|
|
+ finalFramerate = 1f; // 使用1帧/秒作为最低帧率
|
|
|
+
|
|
|
+ float time = ignoreTimeScale ? Time.unscaledTime : Time.time;
|
|
|
+ float interval = 1.0f / finalFramerate;
|
|
|
+
|
|
|
+ if (time - timer > interval)
|
|
|
+ {
|
|
|
+ DoUpdate();
|
|
|
+ timer = time;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 执行帧更新
|
|
|
+ /// </summary>
|
|
|
+ private void DoUpdate()
|
|
|
+ {
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ if (currentAnim == null) return;
|
|
|
+
|
|
|
+ // 更新帧索引
|
|
|
+ currentFrameIndex++;
|
|
|
+
|
|
|
+ // 检查是否播放完成
|
|
|
+ if (currentFrameIndex >= currentAnim.frames.Length)
|
|
|
+ {
|
|
|
+ if (currentAnim.loop)
|
|
|
+ {
|
|
|
+ currentFrameIndex = 0; // 循环播放
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ currentFrameIndex = currentAnim.frames.Length - 1;
|
|
|
+ Stop();
|
|
|
+ OnAnimationComplete?.Invoke();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新显示的帧
|
|
|
+ UpdateFrame();
|
|
|
+
|
|
|
+ // 触发帧变化事件
|
|
|
+ OnFrameChanged?.Invoke(currentFrameIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 更新当前显示的帧
|
|
|
+ /// </summary>
|
|
|
+ private void UpdateFrame()
|
|
|
+ {
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ if (currentAnim?.frames == null || currentFrameIndex < 0 || currentFrameIndex >= currentAnim.frames.Length)
|
|
|
+ return;
|
|
|
+
|
|
|
+ Sprite currentSprite = currentAnim.frames[currentFrameIndex];
|
|
|
+
|
|
|
+ // 获取实际使用的渲染组件
|
|
|
+ SpriteRenderer targetSpriteRenderer = currentAnim.GetSpriteRenderer(spriteRenderer);
|
|
|
+ Image targetImage = currentAnim.GetImage(image);
|
|
|
+
|
|
|
+ // 更新SpriteRenderer
|
|
|
+ if (targetSpriteRenderer != null)
|
|
|
+ {
|
|
|
+ targetSpriteRenderer.sprite = currentSprite;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新Image
|
|
|
+ if (targetImage != null)
|
|
|
+ {
|
|
|
+ targetImage.sprite = currentSprite;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 开始播放当前动画
|
|
|
+ /// </summary>
|
|
|
+ public void Play()
|
|
|
+ {
|
|
|
+ if (!HasValidAnimation())
|
|
|
+ {
|
|
|
+ Debug.LogWarning("No valid animation to play!");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 应用当前动画的渲染设置
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ if (currentAnim != null)
|
|
|
+ {
|
|
|
+ currentAnim.StoreOriginalValues(spriteRenderer, image);
|
|
|
+ currentAnim.ApplyRenderSettings(spriteRenderer, image);
|
|
|
+ }
|
|
|
+
|
|
|
+ isPlaying = true;
|
|
|
+ timer = ignoreTimeScale ? Time.unscaledTime : Time.time;
|
|
|
+ UpdateFrame();
|
|
|
+ OnAnimationStart?.Invoke();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 播放指定索引的动画
|
|
|
+ /// </summary>
|
|
|
+ public void PlayAnimation(int animationIndex)
|
|
|
+ {
|
|
|
+ if (SetAnimation(animationIndex))
|
|
|
+ {
|
|
|
+ Play();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 播放指定名称的动画
|
|
|
+ /// </summary>
|
|
|
+ public void PlayAnimation(string animationName)
|
|
|
+ {
|
|
|
+ int index = GetAnimationIndex(animationName);
|
|
|
+ if (index >= 0)
|
|
|
+ {
|
|
|
+ PlayAnimation(index);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.LogWarning($"Animation '{animationName}' not found!");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 停止播放
|
|
|
+ /// </summary>
|
|
|
+ public void Stop()
|
|
|
+ {
|
|
|
+ isPlaying = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 暂停播放
|
|
|
+ /// </summary>
|
|
|
+ public void Pause()
|
|
|
+ {
|
|
|
+ isPlaying = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 重置到第一帧
|
|
|
+ /// </summary>
|
|
|
+ public void Reset()
|
|
|
+ {
|
|
|
+ currentFrameIndex = 0;
|
|
|
+ UpdateFrame();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 跳转到指定帧
|
|
|
+ /// </summary>
|
|
|
+ public void SetFrame(int frameIndex)
|
|
|
+ {
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ if (currentAnim?.frames == null || frameIndex < 0 || frameIndex >= currentAnim.frames.Length)
|
|
|
+ return;
|
|
|
+
|
|
|
+ currentFrameIndex = frameIndex;
|
|
|
+ UpdateFrame();
|
|
|
+ OnFrameChanged?.Invoke(currentFrameIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置当前动画
|
|
|
+ /// </summary>
|
|
|
+ public bool SetAnimation(int animationIndex)
|
|
|
+ {
|
|
|
+ if (animations == null || animationIndex < 0 || animationIndex >= animations.Count)
|
|
|
+ {
|
|
|
+ Debug.LogWarning($"Invalid animation index: {animationIndex}");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (currentAnimationIndex != animationIndex)
|
|
|
+ {
|
|
|
+ // 恢复之前动画的设置
|
|
|
+ var previousAnim = GetCurrentAnimation();
|
|
|
+ if (previousAnim != null)
|
|
|
+ {
|
|
|
+ previousAnim.RestoreOriginalValues(spriteRenderer, image);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 切换到新动画
|
|
|
+ currentAnimationIndex = animationIndex;
|
|
|
+
|
|
|
+ // 存储并应用新动画的设置
|
|
|
+ var newAnim = GetCurrentAnimation();
|
|
|
+ if (newAnim != null)
|
|
|
+ {
|
|
|
+ newAnim.StoreOriginalValues(spriteRenderer, image);
|
|
|
+ newAnim.ApplyRenderSettings(spriteRenderer, image);
|
|
|
+ }
|
|
|
+
|
|
|
+ Reset();
|
|
|
+ OnAnimationChanged?.Invoke(currentAnimationIndex, CurrentAnimationName);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置当前动画(通过名称)
|
|
|
+ /// </summary>
|
|
|
+ public bool SetAnimation(string animationName)
|
|
|
+ {
|
|
|
+ int index = GetAnimationIndex(animationName);
|
|
|
+ if (index >= 0)
|
|
|
+ {
|
|
|
+ return SetAnimation(index);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取动画索引(通过名称)
|
|
|
+ /// </summary>
|
|
|
+ public int GetAnimationIndex(string animationName)
|
|
|
+ {
|
|
|
+ if (animations == null) return -1;
|
|
|
+
|
|
|
+ for (int i = 0; i < animations.Count; i++)
|
|
|
+ {
|
|
|
+ if (animations[i].name == animationName)
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 添加新的动画序列
|
|
|
+ /// </summary>
|
|
|
+ public void AddAnimation(AnimationSequence newAnimation)
|
|
|
+ {
|
|
|
+ if (animations == null)
|
|
|
+ animations = new List<AnimationSequence>();
|
|
|
+ animations.Add(newAnimation);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 添加新的动画序列
|
|
|
+ /// </summary>
|
|
|
+ public void AddAnimation(string name, Sprite[] frames, float framerate = 30f, bool loop = true)
|
|
|
+ {
|
|
|
+ AddAnimation(new AnimationSequence(name, frames, framerate, loop));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 移除动画序列
|
|
|
+ /// </summary>
|
|
|
+ public bool RemoveAnimation(int index)
|
|
|
+ {
|
|
|
+ if (animations == null || index < 0 || index >= animations.Count)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ animations.RemoveAt(index);
|
|
|
+
|
|
|
+ // 调整当前动画索引
|
|
|
+ if (currentAnimationIndex >= animations.Count)
|
|
|
+ {
|
|
|
+ currentAnimationIndex = Mathf.Max(0, animations.Count - 1);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 移除动画序列
|
|
|
+ /// </summary>
|
|
|
+ public bool RemoveAnimation(string animationName)
|
|
|
+ {
|
|
|
+ int index = GetAnimationIndex(animationName);
|
|
|
+ return index >= 0 && RemoveAnimation(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取所有动画名称
|
|
|
+ /// </summary>
|
|
|
+ public string[] GetAnimationNames()
|
|
|
+ {
|
|
|
+ if (animations == null) return new string[0];
|
|
|
+
|
|
|
+ string[] names = new string[animations.Count];
|
|
|
+ for (int i = 0; i < animations.Count; i++)
|
|
|
+ {
|
|
|
+ names[i] = animations[i].name;
|
|
|
+ }
|
|
|
+ return names;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取当前动画使用的SpriteRenderer
|
|
|
+ /// </summary>
|
|
|
+ public SpriteRenderer GetCurrentSpriteRenderer()
|
|
|
+ {
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ return currentAnim?.GetSpriteRenderer(spriteRenderer);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取当前动画使用的Image
|
|
|
+ /// </summary>
|
|
|
+ public Image GetCurrentImage()
|
|
|
+ {
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ return currentAnim?.GetImage(image);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置当前动画的渲染颜色
|
|
|
+ /// </summary>
|
|
|
+ public void SetCurrentAnimationColor(Color color)
|
|
|
+ {
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ if (currentAnim != null)
|
|
|
+ {
|
|
|
+ currentAnim.tintColor = color;
|
|
|
+
|
|
|
+ // 立即应用
|
|
|
+ SpriteRenderer targetSpriteRenderer = currentAnim.GetSpriteRenderer(spriteRenderer);
|
|
|
+ Image targetImage = currentAnim.GetImage(image);
|
|
|
+
|
|
|
+ if (targetSpriteRenderer != null)
|
|
|
+ targetSpriteRenderer.color = color;
|
|
|
+ if (targetImage != null)
|
|
|
+ targetImage.color = color;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置当前动画的排序层级
|
|
|
+ /// </summary>
|
|
|
+ public void SetCurrentAnimationSortingOrder(int order)
|
|
|
+ {
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ if (currentAnim != null)
|
|
|
+ {
|
|
|
+ currentAnim.sortingOrder = order;
|
|
|
+ currentAnim.overrideSortingOrder = true;
|
|
|
+
|
|
|
+ // 立即应用
|
|
|
+ SpriteRenderer targetSpriteRenderer = currentAnim.GetSpriteRenderer(spriteRenderer);
|
|
|
+ if (targetSpriteRenderer != null)
|
|
|
+ targetSpriteRenderer.sortingOrder = order;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置当前动画的缩放
|
|
|
+ /// </summary>
|
|
|
+ public void SetCurrentAnimationScale(Vector3 scale)
|
|
|
+ {
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ if (currentAnim != null)
|
|
|
+ {
|
|
|
+ currentAnim.customScale = scale;
|
|
|
+ currentAnim.useCustomScale = true;
|
|
|
+
|
|
|
+ // 立即应用
|
|
|
+ SpriteRenderer targetSpriteRenderer = currentAnim.GetSpriteRenderer(spriteRenderer);
|
|
|
+ Image targetImage = currentAnim.GetImage(image);
|
|
|
+
|
|
|
+ if (targetSpriteRenderer != null)
|
|
|
+ targetSpriteRenderer.transform.localScale = scale;
|
|
|
+ if (targetImage != null)
|
|
|
+ targetImage.transform.localScale = scale;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 为指定动画设置目标渲染组件
|
|
|
+ /// </summary>
|
|
|
+ public void SetAnimationRenderer(int animationIndex, SpriteRenderer targetRenderer, Image targetImg = null)
|
|
|
+ {
|
|
|
+ if (animations == null || animationIndex < 0 || animationIndex >= animations.Count)
|
|
|
+ return;
|
|
|
+
|
|
|
+ animations[animationIndex].targetSpriteRenderer = targetRenderer;
|
|
|
+ animations[animationIndex].targetImage = targetImg;
|
|
|
+ animations[animationIndex].useGlobalRenderer = (targetRenderer == null && targetImg == null);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 为指定动画设置目标渲染组件(通过名称)
|
|
|
+ /// </summary>
|
|
|
+ public void SetAnimationRenderer(string animationName, SpriteRenderer targetRenderer, Image targetImg = null)
|
|
|
+ {
|
|
|
+ int index = GetAnimationIndex(animationName);
|
|
|
+ if (index >= 0)
|
|
|
+ {
|
|
|
+ SetAnimationRenderer(index, targetRenderer, targetImg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 启用/禁用组件
|
|
|
+ /// </summary>
|
|
|
+ private void OnEnable()
|
|
|
+ {
|
|
|
+ if (playOnStart && HasValidAnimation())
|
|
|
+ {
|
|
|
+ Play();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void OnDisable()
|
|
|
+ {
|
|
|
+ Stop();
|
|
|
+
|
|
|
+ // 恢复原始设置
|
|
|
+ var currentAnim = GetCurrentAnimation();
|
|
|
+ if (currentAnim != null)
|
|
|
+ {
|
|
|
+ currentAnim.RestoreOriginalValues(spriteRenderer, image);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## SequenceFramePlayerExample
|
|
|
+
|
|
|
+``` cs
|
|
|
+using System.Collections;
|
|
|
+using UnityEngine;
|
|
|
+using UnityEngine.UI;
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// SequenceFramePlayer 使用示例
|
|
|
+/// </summary>
|
|
|
+public class SequenceFramePlayerExample : MonoBehaviour
|
|
|
+{
|
|
|
+ [Header("示例组件")]
|
|
|
+ public SequenceFramePlayer framePlayer;
|
|
|
+ public SpriteRenderer backgroundRenderer;
|
|
|
+ public SpriteRenderer characterRenderer;
|
|
|
+ public Image uiImage;
|
|
|
+
|
|
|
+ [Header("示例序列帧")]
|
|
|
+ public Sprite[] idleFrames;
|
|
|
+ public Sprite[] walkFrames;
|
|
|
+ public Sprite[] attackFrames;
|
|
|
+ public Sprite[] uiFrames;
|
|
|
+
|
|
|
+ private void Start()
|
|
|
+ {
|
|
|
+ // 示例1:基础使用
|
|
|
+ BasicUsageExample();
|
|
|
+
|
|
|
+ // 示例2:多渲染器使用
|
|
|
+ MultiRendererExample();
|
|
|
+
|
|
|
+ // 示例3:运行时动态管理
|
|
|
+ RuntimeManagementExample();
|
|
|
+
|
|
|
+ // 示例4:事件监听
|
|
|
+ EventListenerExample();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 示例1:基础使用 - 简单播放动画
|
|
|
+ /// </summary>
|
|
|
+ void BasicUsageExample()
|
|
|
+ {
|
|
|
+ Debug.Log("=== 基础使用示例 ===");
|
|
|
+
|
|
|
+ // 直接播放当前动画(默认第0个)
|
|
|
+ framePlayer.Play();
|
|
|
+
|
|
|
+ // 播放指定索引的动画
|
|
|
+ framePlayer.PlayAnimation(1);
|
|
|
+
|
|
|
+ // 播放指定名称的动画
|
|
|
+ framePlayer.PlayAnimation("idle");
|
|
|
+
|
|
|
+ // 控制播放
|
|
|
+ StartCoroutine(BasicPlaybackControl());
|
|
|
+ }
|
|
|
+
|
|
|
+ IEnumerator BasicPlaybackControl()
|
|
|
+ {
|
|
|
+ yield return new WaitForSeconds(2f);
|
|
|
+
|
|
|
+ // 暂停
|
|
|
+ framePlayer.Pause();
|
|
|
+ Debug.Log("动画暂停");
|
|
|
+
|
|
|
+ yield return new WaitForSeconds(1f);
|
|
|
+
|
|
|
+ // 继续播放
|
|
|
+ framePlayer.Play();
|
|
|
+ Debug.Log("动画继续");
|
|
|
+
|
|
|
+ yield return new WaitForSeconds(2f);
|
|
|
+
|
|
|
+ // 跳转到指定帧
|
|
|
+ framePlayer.SetFrame(5);
|
|
|
+ Debug.Log("跳转到第5帧");
|
|
|
+
|
|
|
+ yield return new WaitForSeconds(1f);
|
|
|
+
|
|
|
+ // 重置到第一帧
|
|
|
+ framePlayer.Reset();
|
|
|
+ Debug.Log("重置到第一帧");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 示例2:多渲染器使用 - 不同动画渲染到不同组件
|
|
|
+ /// </summary>
|
|
|
+ void MultiRendererExample()
|
|
|
+ {
|
|
|
+ Debug.Log("=== 多渲染器示例 ===");
|
|
|
+
|
|
|
+ // 为不同动画设置不同的渲染器
|
|
|
+ framePlayer.SetAnimationRenderer("background", backgroundRenderer);
|
|
|
+ framePlayer.SetAnimationRenderer("character", characterRenderer);
|
|
|
+ framePlayer.SetAnimationRenderer("ui", null, uiImage); // UI动画渲染到Image
|
|
|
+
|
|
|
+ // 播放背景动画
|
|
|
+ framePlayer.PlayAnimation("background");
|
|
|
+
|
|
|
+ StartCoroutine(SwitchRenderers());
|
|
|
+ }
|
|
|
+
|
|
|
+ IEnumerator SwitchRenderers()
|
|
|
+ {
|
|
|
+ yield return new WaitForSeconds(3f);
|
|
|
+
|
|
|
+ // 切换到角色动画(会自动切换到characterRenderer)
|
|
|
+ framePlayer.PlayAnimation("character");
|
|
|
+ Debug.Log("切换到角色动画");
|
|
|
+
|
|
|
+ yield return new WaitForSeconds(3f);
|
|
|
+
|
|
|
+ // 切换到UI动画(会自动切换到uiImage)
|
|
|
+ framePlayer.PlayAnimation("ui");
|
|
|
+ Debug.Log("切换到UI动画");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 示例3:运行时动态管理 - 动态添加、修改动画
|
|
|
+ /// </summary>
|
|
|
+ void RuntimeManagementExample()
|
|
|
+ {
|
|
|
+ Debug.Log("=== 运行时管理示例 ===");
|
|
|
+
|
|
|
+ // 运行时添加新动画
|
|
|
+ if (idleFrames != null && idleFrames.Length > 0)
|
|
|
+ {
|
|
|
+ framePlayer.AddAnimation("runtime_idle", idleFrames, 24f, true);
|
|
|
+ Debug.Log("添加了运行时空闲动画");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (walkFrames != null && walkFrames.Length > 0)
|
|
|
+ {
|
|
|
+ framePlayer.AddAnimation("runtime_walk", walkFrames, 30f, true);
|
|
|
+ Debug.Log("添加了运行时行走动画");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取所有动画名称
|
|
|
+ string[] animNames = framePlayer.GetAnimationNames();
|
|
|
+ Debug.Log($"当前动画数量: {animNames.Length}");
|
|
|
+ foreach (string name in animNames)
|
|
|
+ {
|
|
|
+ Debug.Log($"动画: {name}");
|
|
|
+ }
|
|
|
+
|
|
|
+ StartCoroutine(RuntimeDynamicControl());
|
|
|
+ }
|
|
|
+
|
|
|
+ IEnumerator RuntimeDynamicControl()
|
|
|
+ {
|
|
|
+ yield return new WaitForSeconds(1f);
|
|
|
+
|
|
|
+ // 播放运行时添加的动画
|
|
|
+ framePlayer.PlayAnimation("runtime_idle");
|
|
|
+ Debug.Log("播放运行时空闲动画");
|
|
|
+
|
|
|
+ yield return new WaitForSeconds(2f);
|
|
|
+
|
|
|
+ // 动态修改当前动画的颜色
|
|
|
+ framePlayer.SetCurrentAnimationColor(Color.red);
|
|
|
+ Debug.Log("设置动画颜色为红色");
|
|
|
+
|
|
|
+ yield return new WaitForSeconds(1f);
|
|
|
+
|
|
|
+ // 动态修改当前动画的缩放
|
|
|
+ framePlayer.SetCurrentAnimationScale(new Vector3(1.5f, 1.5f, 1f));
|
|
|
+ Debug.Log("设置动画缩放为1.5倍");
|
|
|
+
|
|
|
+ yield return new WaitForSeconds(1f);
|
|
|
+
|
|
|
+ // 动态修改排序层级
|
|
|
+ framePlayer.SetCurrentAnimationSortingOrder(10);
|
|
|
+ Debug.Log("设置排序层级为10");
|
|
|
+
|
|
|
+ yield return new WaitForSeconds(2f);
|
|
|
+
|
|
|
+ // 切换到其他动画(会自动恢复原始设置)
|
|
|
+ framePlayer.PlayAnimation("runtime_walk");
|
|
|
+ Debug.Log("切换到行走动画(自动恢复设置)");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 示例4:事件监听 - 监听动画事件
|
|
|
+ /// </summary>
|
|
|
+ void EventListenerExample()
|
|
|
+ {
|
|
|
+ Debug.Log("=== 事件监听示例 ===");
|
|
|
+
|
|
|
+ // 监听动画开始事件
|
|
|
+ framePlayer.OnAnimationStart += () => {
|
|
|
+ Debug.Log($"动画开始播放: {framePlayer.CurrentAnimationName}");
|
|
|
+ };
|
|
|
+
|
|
|
+ // 监听动画完成事件
|
|
|
+ framePlayer.OnAnimationComplete += () => {
|
|
|
+ Debug.Log($"动画播放完成: {framePlayer.CurrentAnimationName}");
|
|
|
+ OnAnimationCompleted();
|
|
|
+ };
|
|
|
+
|
|
|
+ // 监听帧变化事件
|
|
|
+ framePlayer.OnFrameChanged += (frameIndex) => {
|
|
|
+ Debug.Log($"当前帧: {frameIndex}/{framePlayer.TotalFrames},进度: {framePlayer.Progress:P1}");
|
|
|
+ };
|
|
|
+
|
|
|
+ // 监听动画切换事件
|
|
|
+ framePlayer.OnAnimationChanged += (index, name) => {
|
|
|
+ Debug.Log($"动画切换: 索引 {index},名称 '{name}'");
|
|
|
+ OnAnimationSwitched(index, name);
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnAnimationCompleted()
|
|
|
+ {
|
|
|
+ // 动画完成后的逻辑
|
|
|
+ Debug.Log("执行动画完成后的逻辑");
|
|
|
+ }
|
|
|
+
|
|
|
+ void OnAnimationSwitched(int index, string name)
|
|
|
+ {
|
|
|
+ // 动画切换后的逻辑
|
|
|
+ Debug.Log($"动画已切换到: {name}");
|
|
|
+
|
|
|
+ // 获取当前使用的渲染组件
|
|
|
+ SpriteRenderer currentSpriteRenderer = framePlayer.GetCurrentSpriteRenderer();
|
|
|
+ Image currentImage = framePlayer.GetCurrentImage();
|
|
|
+
|
|
|
+ if (currentSpriteRenderer != null)
|
|
|
+ Debug.Log($"当前使用SpriteRenderer: {currentSpriteRenderer.name}");
|
|
|
+ if (currentImage != null)
|
|
|
+ Debug.Log($"当前使用Image: {currentImage.name}");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 键盘控制示例
|
|
|
+ /// </summary>
|
|
|
+ void Update()
|
|
|
+ {
|
|
|
+ // 数字键1-4控制动画播放
|
|
|
+ if (Input.GetKeyDown(KeyCode.Alpha1))
|
|
|
+ {
|
|
|
+ framePlayer.PlayAnimation(0);
|
|
|
+ Debug.Log("播放动画 0");
|
|
|
+ }
|
|
|
+ if (Input.GetKeyDown(KeyCode.Alpha2))
|
|
|
+ {
|
|
|
+ framePlayer.PlayAnimation(1);
|
|
|
+ Debug.Log("播放动画 1");
|
|
|
+ }
|
|
|
+ if (Input.GetKeyDown(KeyCode.Alpha3))
|
|
|
+ {
|
|
|
+ framePlayer.PlayAnimation(2);
|
|
|
+ Debug.Log("播放动画 2");
|
|
|
+ }
|
|
|
+ if (Input.GetKeyDown(KeyCode.Alpha4))
|
|
|
+ {
|
|
|
+ framePlayer.PlayAnimation(3);
|
|
|
+ Debug.Log("播放动画 3");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 空格键暂停/播放
|
|
|
+ if (Input.GetKeyDown(KeyCode.Space))
|
|
|
+ {
|
|
|
+ if (framePlayer.IsPlaying)
|
|
|
+ {
|
|
|
+ framePlayer.Pause();
|
|
|
+ Debug.Log("暂停动画");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ framePlayer.Play();
|
|
|
+ Debug.Log("继续动画");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // R键重置
|
|
|
+ if (Input.GetKeyDown(KeyCode.R))
|
|
|
+ {
|
|
|
+ framePlayer.Reset();
|
|
|
+ Debug.Log("重置动画");
|
|
|
+ }
|
|
|
+
|
|
|
+ // C键改变颜色
|
|
|
+ if (Input.GetKeyDown(KeyCode.C))
|
|
|
+ {
|
|
|
+ Color randomColor = new Color(Random.value, Random.value, Random.value, 1f);
|
|
|
+ framePlayer.SetCurrentAnimationColor(randomColor);
|
|
|
+ Debug.Log($"设置随机颜色: {randomColor}");
|
|
|
+ }
|
|
|
+
|
|
|
+ // S键改变缩放
|
|
|
+ if (Input.GetKeyDown(KeyCode.S))
|
|
|
+ {
|
|
|
+ float scale = Random.Range(0.5f, 2f);
|
|
|
+ framePlayer.SetCurrentAnimationScale(Vector3.one * scale);
|
|
|
+ Debug.Log($"设置缩放: {scale}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 高级用法示例 - 创建复杂的动画序列
|
|
|
+ /// </summary>
|
|
|
+ public void AdvancedUsageExample()
|
|
|
+ {
|
|
|
+ Debug.Log("=== 高级用法示例 ===");
|
|
|
+
|
|
|
+ // 创建一个复杂的动画序列
|
|
|
+ var complexAnim = new AnimationSequence();
|
|
|
+ complexAnim.name = "complex_animation";
|
|
|
+ complexAnim.frames = attackFrames;
|
|
|
+ complexAnim.framerate = 60f;
|
|
|
+ complexAnim.loop = false;
|
|
|
+
|
|
|
+ // 设置渲染属性
|
|
|
+ complexAnim.useGlobalRenderer = false;
|
|
|
+ complexAnim.targetSpriteRenderer = characterRenderer;
|
|
|
+ complexAnim.tintColor = Color.yellow;
|
|
|
+ complexAnim.useCustomScale = true;
|
|
|
+ complexAnim.customScale = new Vector3(2f, 2f, 1f);
|
|
|
+ complexAnim.overrideSortingOrder = true;
|
|
|
+ complexAnim.sortingOrder = 100;
|
|
|
+
|
|
|
+ // 设置曲线控制
|
|
|
+ complexAnim.useCurve = true;
|
|
|
+ complexAnim.curve = AnimationCurve.EaseInOut(0f, 0.5f, 1f, 2f); // 先慢后快
|
|
|
+
|
|
|
+ // 添加到播放器
|
|
|
+ framePlayer.AddAnimation(complexAnim);
|
|
|
+
|
|
|
+ // 播放这个复杂动画
|
|
|
+ framePlayer.PlayAnimation("complex_animation");
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|