using UnityEngine; using System.Collections.Generic; using Sirenix.OdinInspector; // 引入 Odin 命名空间 #if UNITY_EDITOR using UnityEditor.Animations; // 仅编辑器下引用,用于解析Controller #endif namespace Cielonos.MainGame.Characters { public class AnimatorStateMapper { // --- 配置区域 --- [Title("Bake Settings")] [SerializeField, LabelText("Target Animator")] private Animator _targetAnimatorForBake; [TableList(ShowIndexLabels = true), Searchable] // Odin: 列表显示为表格,且支持搜索 [SerializeField] private List _mappings = new List(); // --- 运行时缓存 --- private Dictionary _clipDict; private bool _isInitialized = false; // --- 简单的数据结构 --- [System.Serializable] public struct StateClipPair { [ReadOnly] // 防止手动误改,建议通过Bake生成 public string stateName; public AnimationClip clip; } public AnimatorStateMapper() { _mappings = new List(); } // ======================================================================== // Runtime API (供宿主调用) // ======================================================================== /// /// 必须在宿主的 Awake 中调用此方法构建索引 /// public void Initialize() { if (_isInitialized) return; _clipDict = new Dictionary(_mappings.Count); foreach (var pair in _mappings) { if (!string.IsNullOrEmpty(pair.stateName) && pair.clip != null) { // 防止重复Key报错,以后面的覆盖前面的(或者你可以选择忽略) _clipDict[pair.stateName] = pair.clip; } } _isInitialized = true; } public AnimationClip GetClip(string stateName) { if (!_isInitialized) Initialize(); if (_clipDict.TryGetValue(stateName, out var clip)) { return clip; } Debug.LogWarning($"[AnimatorStateMapper] 未找到 State: '{stateName}' 对应的 Clip。请检查是否已 Bake 或 State 名字是否正确。"); return null; } public float GetClipLength(string stateName) { var clip = GetClip(stateName); return clip != null ? clip.length : 0f; } // ======================================================================== // Editor Baking Logic (Odin Button) // ======================================================================== #if UNITY_EDITOR public void Bake(Animator animator) { _targetAnimatorForBake = animator; var controller = _targetAnimatorForBake.runtimeAnimatorController as AnimatorController; // 处理 Override Controller 的情况 if (controller == null && _targetAnimatorForBake.runtimeAnimatorController is AnimatorOverrideController overrideCtrl) { controller = overrideCtrl.runtimeAnimatorController as AnimatorController; } if (controller == null) { Debug.LogError("Animator 上未找到有效的 AnimatorController (或 AnimatorOverrideController) 资源!"); return; } _mappings.Clear(); int count = 0; // 递归遍历所有的 Layer 和 SubStateMachine foreach (var layer in controller.layers) { count += RecursiveProcessStateMachine(layer.stateMachine); } Debug.Log($"Bake 完成!共提取了 {count} 个 State-Clip 映射。"); } private int RecursiveProcessStateMachine(AnimatorStateMachine stateMachine) { int count = 0; // 1. 遍历当前层级的 State foreach (var childState in stateMachine.states) { var state = childState.state; var motion = state.motion; // 仅处理直接引用 AnimationClip 的情况 if (motion is AnimationClip clip) { _mappings.Add(new StateClipPair { stateName = state.name, clip = clip }); count++; } // TODO: 如果需要支持 BlendTree,可以在这里扩展逻辑 // else if (motion is BlendTree tree) { ... } } // 2. 递归遍历子状态机 (Sub-State Machines) foreach (var childMachine in stateMachine.stateMachines) { count += RecursiveProcessStateMachine(childMachine.stateMachine); } return count; } #endif } #if UNITY_EDITOR public partial class AnimationSubcontrollerBase { [Button] private void MapperBake() { mapper ??= new AnimatorStateMapper(); mapper.Bake(animator); } } #endif }