unity动画序列帧代码实现 代码缓存_第二章.md 21 KB

组合多序列帧动画播放

MultiSequenceFramePlayer

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}' 不存在!");
        }
    }
} 

MultiSequenceExample

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("调试渲染器设置");
        }
    }
}