Files
Cielonos/Assets/Scripts/MainGame/Characters/Base/Subcontrollers/Animation/AnimatorClipMap.cs
SoulliesOfficial 9a9e48f8a5
2026-06-27 12:52:03 -04:00

139 lines
5.1 KiB
C#

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
{
/// <summary>
/// 将 Animator 状态机中的 State 名称映射到对应 AnimationClip 的查询器。
/// 通过 Bake 按钮在编辑器中预先建立映射表,运行时以 O(1) 速度查询。
/// </summary>
[System.Serializable]
public class AnimatorStateMapper
{
[Title("Bake Settings")]
[SerializeField, LabelText("Target Animator")]
private Animator _targetAnimatorForBake;
[TableList(ShowIndexLabels = true), Searchable]
[SerializeField]
private List<StateClipPair> _mappings = new List<StateClipPair>();
private Dictionary<string, AnimationClip> _clipDict;
private bool _isInitialized = false;
[System.Serializable]
public struct StateClipPair
{
[ReadOnly] public string stateName;
public AnimationClip clip;
}
public AnimatorStateMapper()
{
_mappings = new List<StateClipPair>();
}
// ── Runtime API ───────────────────────────────────────────────────
/// <summary>必须在宿主的 Awake / Initialize 中调用,构建运行时索引。</summary>
public void Initialize()
{
if (_isInitialized) return;
_clipDict = new Dictionary<string, AnimationClip>(_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($"<color=green>Bake 完成!共提取了 {count} 个 State-Clip 映射。</color>");
}
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
}