组合多序列帧动画播放
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 播放状态数据
/// </summary>
[System.Serializable]
public class AnimationPlayState
{
public int animationIndex;
public int currentFrameIndex;
public float timer;
public bool isPlaying;
public bool isPaused;
public AnimationPlayState(int animIndex)
{
animationIndex = animIndex;
currentFrameIndex = 0;
timer = 0f;
isPlaying = false;
isPaused = false;
}
}
/// <summary>
/// 多序列帧动画播放器 - 支持同时播放多个动画
/// </summary>
public class MultiSequenceFramePlayer : MonoBehaviour
{
[Header("动画序列")]
[SerializeField] private List<AnimationSequence> animations = new List<AnimationSequence>();
[SerializeField] private bool playAllOnStart = false; // 启动时播放所有动画
[SerializeField] private bool ignoreTimeScale = false;
[Header("全局渲染组件")]
[SerializeField] private SpriteRenderer globalSpriteRenderer;
[SerializeField] private Image globalImage;
// 播放状态管理
private Dictionary<int, AnimationPlayState> playStates = new Dictionary<int, AnimationPlayState>();
private Dictionary<int, AnimationSequence> storedOriginalStates = new Dictionary<int, AnimationSequence>();
// 属性
public int AnimationCount => animations?.Count ?? 0;
public bool HasAnyPlaying => playStates.Values.Any(state => state.isPlaying);
// 事件
public System.Action<int, string> OnAnimationStarted; // 动画开始 (索引, 名称)
public System.Action<int, string> OnAnimationCompleted; // 动画完成 (索引, 名称)
public System.Action<int, int> OnFrameChanged; // 帧变化 (动画索引, 帧索引)
private void Awake()
{
// 自动获取组件
if (globalSpriteRenderer == null)
globalSpriteRenderer = GetComponent<SpriteRenderer>();
if (globalImage == null)
globalImage = GetComponent<Image>();
}
private void Start()
{
if (playAllOnStart)
{
PlayAllAnimations();
}
}
private void Update()
{
if (playStates.Count == 0) return;
float currentTime = ignoreTimeScale ? Time.unscaledTime : Time.time;
// 更新所有正在播放的动画
var playingStates = playStates.Values.Where(state => state.isPlaying && !state.isPaused).ToList();
foreach (var playState in playingStates)
{
UpdateAnimationState(playState, currentTime);
}
}
/// <summary>
/// 更新单个动画状态
/// </summary>
private void UpdateAnimationState(AnimationPlayState playState, float currentTime)
{
if (playState.animationIndex >= animations.Count) return;
var animation = animations[playState.animationIndex];
if (animation?.frames == null || animation.frames.Length == 0) return;
// 计算帧率
float finalFramerate = animation.framerate;
if (animation.useCurve && animation.curve != null)
{
float progress = (float)playState.currentFrameIndex / animation.frames.Length;
float curveValue = animation.curve.Evaluate(progress);
finalFramerate = curveValue * animation.framerate;
}
// 检查是否需要更新帧
if (finalFramerate > 0.01f)
{
float interval = 1.0f / finalFramerate;
if (currentTime - playState.timer > interval)
{
playState.timer = currentTime;
UpdateFrame(playState, animation);
}
}
}
/// <summary>
/// 更新帧
/// </summary>
private void UpdateFrame(AnimationPlayState playState, AnimationSequence animation)
{
// 更新帧索引
playState.currentFrameIndex++;
// 检查是否播放完成
if (playState.currentFrameIndex >= animation.frames.Length)
{
if (animation.loop)
{
playState.currentFrameIndex = 0; // 循环播放
}
else
{
playState.currentFrameIndex = animation.frames.Length - 1;
playState.isPlaying = false;
OnAnimationCompleted?.Invoke(playState.animationIndex, animation.name);
return;
}
}
// 更新显示的帧
UpdateAnimationFrame(playState.animationIndex, animation, playState.currentFrameIndex);
// 触发帧变化事件
OnFrameChanged?.Invoke(playState.animationIndex, playState.currentFrameIndex);
}
/// <summary>
/// 更新动画帧显示
/// </summary>
private void UpdateAnimationFrame(int animationIndex, AnimationSequence animation, int frameIndex)
{
if (frameIndex < 0 || frameIndex >= animation.frames.Length) return;
Sprite currentSprite = animation.frames[frameIndex];
// 获取目标渲染组件
SpriteRenderer targetSpriteRenderer = animation.GetSpriteRenderer(globalSpriteRenderer);
Image targetImage = animation.GetImage(globalImage);
// 更新渲染组件
if (targetSpriteRenderer != null)
{
targetSpriteRenderer.sprite = currentSprite;
}
if (targetImage != null)
{
targetImage.sprite = currentSprite;
}
}
/// <summary>
/// 播放指定动画
/// </summary>
public bool PlayAnimation(int animationIndex)
{
if (animationIndex < 0 || animationIndex >= animations.Count) return false;
var animation = animations[animationIndex];
if (animation?.frames == null || animation.frames.Length == 0) return false;
// 创建或更新播放状态
if (!playStates.ContainsKey(animationIndex))
{
playStates[animationIndex] = new AnimationPlayState(animationIndex);
}
var playState = playStates[animationIndex];
playState.isPlaying = true;
playState.isPaused = false;
playState.currentFrameIndex = 0;
playState.timer = ignoreTimeScale ? Time.unscaledTime : Time.time;
// 获取实际的目标渲染器
SpriteRenderer targetSpriteRenderer = animation.GetSpriteRenderer(globalSpriteRenderer);
Image targetImage = animation.GetImage(globalImage);
// 存储并应用渲染设置
animation.StoreOriginalValues(targetSpriteRenderer, targetImage);
animation.ApplyRenderSettings(targetSpriteRenderer, targetImage);
// 立即更新第一帧
UpdateAnimationFrame(animationIndex, animation, 0);
OnAnimationStarted?.Invoke(animationIndex, animation.name);
return true;
}
/// <summary>
/// 播放指定名称的动画
/// </summary>
public bool PlayAnimation(string animationName)
{
int index = GetAnimationIndex(animationName);
return index >= 0 && PlayAnimation(index);
}
/// <summary>
/// 播放所有动画
/// </summary>
public void PlayAllAnimations()
{
for (int i = 0; i < animations.Count; i++)
{
PlayAnimation(i);
}
}
/// <summary>
/// 暂停指定动画
/// </summary>
public void PauseAnimation(int animationIndex)
{
if (playStates.ContainsKey(animationIndex))
{
playStates[animationIndex].isPaused = true;
}
}
/// <summary>
/// 暂停指定名称的动画
/// </summary>
public void PauseAnimation(string animationName)
{
int index = GetAnimationIndex(animationName);
if (index >= 0) PauseAnimation(index);
}
/// <summary>
/// 暂停所有动画
/// </summary>
public void PauseAllAnimations()
{
foreach (var state in playStates.Values)
{
state.isPaused = true;
}
}
/// <summary>
/// 恢复指定动画
/// </summary>
public void ResumeAnimation(int animationIndex)
{
if (playStates.ContainsKey(animationIndex))
{
playStates[animationIndex].isPaused = false;
}
}
/// <summary>
/// 恢复指定名称的动画
/// </summary>
public void ResumeAnimation(string animationName)
{
int index = GetAnimationIndex(animationName);
if (index >= 0) ResumeAnimation(index);
}
/// <summary>
/// 恢复所有动画
/// </summary>
public void ResumeAllAnimations()
{
foreach (var state in playStates.Values)
{
state.isPaused = false;
}
}
/// <summary>
/// 停止指定动画
/// </summary>
public void StopAnimation(int animationIndex)
{
if (playStates.ContainsKey(animationIndex))
{
playStates[animationIndex].isPlaying = false;
playStates[animationIndex].isPaused = false;
// 恢复原始设置
if (animationIndex < animations.Count)
{
var animation = animations[animationIndex];
SpriteRenderer targetSpriteRenderer = animation.GetSpriteRenderer(globalSpriteRenderer);
Image targetImage = animation.GetImage(globalImage);
animation.RestoreOriginalValues(targetSpriteRenderer, targetImage);
}
}
}
/// <summary>
/// 停止指定名称的动画
/// </summary>
public void StopAnimation(string animationName)
{
int index = GetAnimationIndex(animationName);
if (index >= 0) StopAnimation(index);
}
/// <summary>
/// 停止所有动画
/// </summary>
public void StopAllAnimations()
{
foreach (var kvp in playStates)
{
StopAnimation(kvp.Key);
}
playStates.Clear();
}
/// <summary>
/// 检查动画是否正在播放
/// </summary>
public bool IsAnimationPlaying(int animationIndex)
{
return playStates.ContainsKey(animationIndex) &&
playStates[animationIndex].isPlaying &&
!playStates[animationIndex].isPaused;
}
/// <summary>
/// 检查动画是否正在播放
/// </summary>
public bool IsAnimationPlaying(string animationName)
{
int index = GetAnimationIndex(animationName);
return index >= 0 && IsAnimationPlaying(index);
}
/// <summary>
/// 获取动画当前帧
/// </summary>
public int GetCurrentFrame(int animationIndex)
{
return playStates.ContainsKey(animationIndex) ? playStates[animationIndex].currentFrameIndex : -1;
}
/// <summary>
/// 获取动画当前帧
/// </summary>
public int GetCurrentFrame(string animationName)
{
int index = GetAnimationIndex(animationName);
return index >= 0 ? GetCurrentFrame(index) : -1;
}
/// <summary>
/// 设置动画帧
/// </summary>
public void SetAnimationFrame(int animationIndex, int frameIndex)
{
if (playStates.ContainsKey(animationIndex) && animationIndex < animations.Count)
{
var animation = animations[animationIndex];
if (animation?.frames != null && frameIndex >= 0 && frameIndex < animation.frames.Length)
{
playStates[animationIndex].currentFrameIndex = frameIndex;
UpdateAnimationFrame(animationIndex, animation, frameIndex);
}
}
}
/// <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 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 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>
/// 获取正在播放的动画列表
/// </summary>
public List<string> GetPlayingAnimations()
{
var playingNames = new List<string>();
foreach (var kvp in playStates)
{
if (kvp.Value.isPlaying && !kvp.Value.isPaused)
{
if (kvp.Key < animations.Count)
playingNames.Add(animations[kvp.Key].name);
}
}
return playingNames;
}
/// <summary>
/// 调试方法:检查动画的目标渲染器设置
/// </summary>
public void DebugAnimationRenderers()
{
Debug.Log("=== 动画渲染器设置调试 ===");
for (int i = 0; i < animations.Count; i++)
{
var anim = animations[i];
var targetSpriteRenderer = anim.GetSpriteRenderer(globalSpriteRenderer);
var targetImage = anim.GetImage(globalImage);
Debug.Log($"动画 '{anim.name}' (索引 {i}):");
Debug.Log($" - useGlobalRenderer: {anim.useGlobalRenderer}");
Debug.Log($" - targetSpriteRenderer: {(anim.targetSpriteRenderer != null ? anim.targetSpriteRenderer.name : "null")}");
Debug.Log($" - targetImage: {(anim.targetImage != null ? anim.targetImage.name : "null")}");
Debug.Log($" - 实际使用的SpriteRenderer: {(targetSpriteRenderer != null ? targetSpriteRenderer.name : "null")}");
Debug.Log($" - 实际使用的Image: {(targetImage != null ? targetImage.name : "null")}");
}
}
/// <summary>
/// 为指定动画设置目标渲染器
/// </summary>
public void SetAnimationRenderer(int animationIndex, SpriteRenderer targetRenderer, Image targetImg = null)
{
if (animationIndex >= 0 && animationIndex < animations.Count)
{
animations[animationIndex].targetSpriteRenderer = targetRenderer;
animations[animationIndex].targetImage = targetImg;
animations[animationIndex].useGlobalRenderer = (targetRenderer == null && targetImg == null);
Debug.Log($"设置动画 '{animations[animationIndex].name}' 的渲染器: " +
$"SpriteRenderer={targetRenderer?.name}, Image={targetImg?.name}, useGlobal={animations[animationIndex].useGlobalRenderer}");
}
}
/// <summary>
/// 为指定动画设置目标渲染器(通过名称)
/// </summary>
public void SetAnimationRenderer(string animationName, SpriteRenderer targetRenderer, Image targetImg = null)
{
int index = GetAnimationIndex(animationName);
if (index >= 0)
{
SetAnimationRenderer(index, targetRenderer, targetImg);
}
else
{
Debug.LogWarning($"动画 '{animationName}' 不存在!");
}
}
}
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 多序列帧播放器使用示例
/// </summary>
public class MultiSequenceExample : MonoBehaviour
{
[Header("多序列播放器")]
public MultiSequenceFramePlayer multiPlayer;
[Header("序列帧资源")]
public Sprite[] backgroundFrames;
public Sprite[] symbol1Frames;
public Sprite[] symbol2Frames;
public Sprite[] symbol3Frames;
public Sprite[] effectFrames;
[Header("渲染器")]
public SpriteRenderer backgroundRenderer;
public SpriteRenderer[] symbolRenderers = new SpriteRenderer[3];
public SpriteRenderer effectRenderer;
private void Start()
{
SetupAnimations();
StartCoroutine(DemoSequence());
}
void SetupAnimations()
{
// 方法1:直接创建AnimationSequence时设置
var bgAnim = new AnimationSequence("background", backgroundFrames, 15f, true);
bgAnim.useGlobalRenderer = false;
bgAnim.targetSpriteRenderer = backgroundRenderer;
bgAnim.overrideSortingOrder = true;
bgAnim.sortingOrder = -10;
multiPlayer.AddAnimation(bgAnim);
// 方法2:先添加动画,再设置渲染器(推荐)
multiPlayer.AddAnimation("symbol1", symbol1Frames, 30f, true);
multiPlayer.AddAnimation("symbol2", symbol2Frames, 25f, true);
multiPlayer.AddAnimation("symbol3", symbol3Frames, 35f, true);
multiPlayer.AddAnimation("effect", effectFrames, 60f, false);
// 为每个动画设置独立的渲染器
multiPlayer.SetAnimationRenderer("symbol1", symbolRenderers[0]);
multiPlayer.SetAnimationRenderer("symbol2", symbolRenderers[1]);
multiPlayer.SetAnimationRenderer("symbol3", symbolRenderers[2]);
multiPlayer.SetAnimationRenderer("effect", effectRenderer);
// 调试:打印所有渲染器设置
multiPlayer.DebugAnimationRenderers();
// 监听事件
multiPlayer.OnAnimationStarted += (index, name) => {
Debug.Log($"动画开始: {name}");
};
multiPlayer.OnAnimationCompleted += (index, name) => {
Debug.Log($"动画完成: {name}");
};
}
IEnumerator DemoSequence()
{
// 1. 先播放背景动画
Debug.Log("=== 播放背景动画 ===");
multiPlayer.PlayAnimation("background");
yield return new WaitForSeconds(2f);
// 2. 同时播放所有符号动画
Debug.Log("=== 同时播放所有符号动画 ===");
multiPlayer.PlayAnimation("symbol1");
multiPlayer.PlayAnimation("symbol2");
multiPlayer.PlayAnimation("symbol3");
yield return new WaitForSeconds(3f);
// 3. 暂停第二个符号
Debug.Log("=== 暂停 symbol2 ===");
multiPlayer.PauseAnimation("symbol2");
yield return new WaitForSeconds(2f);
// 4. 恢复第二个符号
Debug.Log("=== 恢复 symbol2 ===");
multiPlayer.ResumeAnimation("symbol2");
yield return new WaitForSeconds(2f);
// 5. 播放特效(不循环)
Debug.Log("=== 播放特效动画 ===");
multiPlayer.PlayAnimation("effect");
yield return new WaitForSeconds(3f);
// 6. 停止所有符号,只保留背景
Debug.Log("=== 停止所有符号 ===");
multiPlayer.StopAnimation("symbol1");
multiPlayer.StopAnimation("symbol2");
multiPlayer.StopAnimation("symbol3");
yield return new WaitForSeconds(2f);
// 7. 显示当前播放状态
var playingAnims = multiPlayer.GetPlayingAnimations();
Debug.Log($"当前播放的动画: {string.Join(", ", playingAnims)}");
}
void Update()
{
// 键盘控制
if (Input.GetKeyDown(KeyCode.Alpha1))
{
multiPlayer.PlayAnimation("symbol1");
Debug.Log("播放 symbol1");
}
if (Input.GetKeyDown(KeyCode.Alpha2))
{
multiPlayer.PlayAnimation("symbol2");
Debug.Log("播放 symbol2");
}
if (Input.GetKeyDown(KeyCode.Alpha3))
{
multiPlayer.PlayAnimation("symbol3");
Debug.Log("播放 symbol3");
}
if (Input.GetKeyDown(KeyCode.B))
{
multiPlayer.PlayAnimation("background");
Debug.Log("播放背景");
}
if (Input.GetKeyDown(KeyCode.E))
{
multiPlayer.PlayAnimation("effect");
Debug.Log("播放特效");
}
if (Input.GetKeyDown(KeyCode.P))
{
multiPlayer.PauseAllAnimations();
Debug.Log("暂停所有动画");
}
if (Input.GetKeyDown(KeyCode.R))
{
multiPlayer.ResumeAllAnimations();
Debug.Log("恢复所有动画");
}
if (Input.GetKeyDown(KeyCode.S))
{
multiPlayer.StopAllAnimations();
Debug.Log("停止所有动画");
}
if (Input.GetKeyDown(KeyCode.A))
{
multiPlayer.PlayAllAnimations();
Debug.Log("播放所有动画");
}
if (Input.GetKeyDown(KeyCode.D))
{
multiPlayer.DebugAnimationRenderers();
Debug.Log("调试渲染器设置");
}
}
}