using UnityEngine; using System.Collections.Generic; using Sirenix.OdinInspector; #if UNITY_EDITOR using UnityEditor.Animations; #endif namespace Cielonos.MainGame.Characters { // ============================================================================ // AnimatorStateMapper (nested in AnimationSubcontrollerBase via partial) // ============================================================================ public partial class AnimationSubcontrollerBase { /// /// 将 Animator 状态机中的 State 名称映射到对应 AnimationClip 的查询器。 /// 通过 Bake 按钮在编辑器中预先建立映射表,运行时以 O(1) 速度查询。 /// [System.Serializable] public class AnimatorStateMapper { [Title("Bake Settings")] [SerializeField, LabelText("Target Animator")] private Animator _targetAnimatorForBake; [TableList(ShowIndexLabels = true), Searchable] [SerializeField] private List _mappings = new List(); private Dictionary _clipDict; private bool _isInitialized = false; [System.Serializable] public struct StateClipPair { [ReadOnly] public string stateName; public AnimationClip clip; } public AnimatorStateMapper() { _mappings = new List(); } // ── Runtime API ─────────────────────────────────────────────────── /// 必须在宿主的 Awake / Initialize 中调用,构建运行时索引。 public void Initialize() { if (_isInitialized) return; _clipDict = new Dictionary(_mappings.Count); foreach (var pair in _mappings) { if (!string.IsNullOrEmpty(pair.stateName) && pair.clip != null) _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 ───────────────────────────────────────────────── #if UNITY_EDITOR public void Bake(Animator animator) { _targetAnimatorForBake = animator; var controller = _targetAnimatorForBake.runtimeAnimatorController as AnimatorController; if (controller == null && _targetAnimatorForBake.runtimeAnimatorController is AnimatorOverrideController oc) controller = oc.runtimeAnimatorController as AnimatorController; if (controller == null) { Debug.LogError("Animator 上未找到有效的 AnimatorController 资源!"); return; } _mappings.Clear(); int count = 0; foreach (var layer in controller.layers) count += RecursiveProcessStateMachine(layer.stateMachine); Debug.Log($"Bake 完成!共提取了 {count} 个 State-Clip 映射。"); } private int RecursiveProcessStateMachine(AnimatorStateMachine sm) { int count = 0; foreach (var cs in sm.states) { if (cs.state.motion is AnimationClip clip) { _mappings.Add(new StateClipPair { stateName = cs.state.name, clip = clip }); count++; } } foreach (var childSm in sm.stateMachines) count += RecursiveProcessStateMachine(childSm.stateMachine); return count; } #endif } } // ── Editor Buttons ───────────────────────────────────────────────────────── #if UNITY_EDITOR public partial class AnimationSubcontrollerBase { [Button] private void MapperBake() { mapper ??= new AnimatorStateMapper(); mapper.Bake(animator); } } #endif }