using System; using System.Collections.Generic; using System.IO; using System.Linq; using Newtonsoft.Json; using UnityEngine; #if UNITY_EDITOR using UnityEditor.Animations; #endif namespace AibisDream.Framework { public static class AnimatorKit { #if UNITY_EDITOR /// /// 解析 AnimatorController 下的所有混合树 /// /// AnimatorController 资产 /// 是否包含嵌套的混合树(子混合树),默认为 true /// 混合树信息列表 public static List ParseBlendTree(AnimatorController controller, bool includeNested = true) { if (controller == null) { Debug.LogWarning("AnimatorController 为空"); return new List(); } var blendTreeInfos = new List(); // 遍历所有层 foreach (var layer in controller.layers) { if (layer.stateMachine != null) { CollectBlendTreesFromStateMachine(layer.stateMachine, blendTreeInfos, includeNested); } } return blendTreeInfos; } /// /// 从状态机中收集混合树(递归处理子状态机) /// private static void CollectBlendTreesFromStateMachine(AnimatorStateMachine stateMachine, List blendTreeInfos, bool includeNested) { if (stateMachine == null) return; // 遍历所有状态 foreach (var state in stateMachine.states) { if (state.state?.motion is BlendTree blendTree) { CollectBlendTreeInfo(blendTree, blendTreeInfos, includeNested); } } // 递归处理子状态机 foreach (var subStateMachine in stateMachine.stateMachines) { if (subStateMachine.stateMachine != null) { CollectBlendTreesFromStateMachine(subStateMachine.stateMachine, blendTreeInfos, includeNested); } } } /// /// 收集混合树信息(可选递归处理子混合树) /// /// 混合树 /// 混合树信息列表 /// 是否包含嵌套的混合树(子混合树) private static void CollectBlendTreeInfo(BlendTree blendTree, List blendTreeInfos, bool includeNested) { if (blendTree == null) return; var info = new BlendTreeInfo { name = blendTree.name, minThreshold = blendTree.minThreshold, maxThreshold = blendTree.maxThreshold, blendParameter = blendTree.blendParameter, blendParameterY = blendTree.blendParameterY }; // 判断混合树类型 int blendType = (int)blendTree.blendType; if (blendType == 0) // Simple1D { info.type = "1D"; info.clips = new List(); if (blendTree.children != null) { foreach (var child in blendTree.children) { if (child.motion != null) { var clipInfo = new BlendTreeClipInfo { clipName = child.motion.name, threshold = child.threshold }; info.clips.Add(clipInfo); // 如果子 Motion 也是 BlendTree,根据 includeNested 参数决定是否递归处理 if (includeNested && child.motion is BlendTree childBlendTree) { CollectBlendTreeInfo(childBlendTree, blendTreeInfos, includeNested); } } } } info.clipCount = info.clips?.Count ?? 0; } else if (blendType >= 1 && blendType <= 3) // 2D 类型 { info.type = "2D"; info.clips = new List(); if (blendTree.children != null) { foreach (var child in blendTree.children) { if (child.motion != null) { var clipInfo = new BlendTreeClipInfo { clipName = child.motion.name, position = child.position }; info.clips.Add(clipInfo); // 如果子 Motion 也是 BlendTree,根据 includeNested 参数决定是否递归处理 if (includeNested && child.motion is BlendTree childBlendTree) { CollectBlendTreeInfo(childBlendTree, blendTreeInfos, includeNested); } } } } } else // Direct 类型或其他 { info.type = "Direct"; info.clipCount = blendTree.children?.Length ?? 0; } blendTreeInfos.Add(info); } #endif /// /// 从 JSON 字符串解析混合树配置(包含默认状态名和混合树列表) /// /// JSON 字符串 /// 混合树配置 public static BlendTreeConfig ParseBlendTreeConfigFromJson(string jsonString) { if (string.IsNullOrEmpty(jsonString)) { Debug.LogWarning("JSON 字符串为空"); return null; } try { // 配置反序列化设置 var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; // 优先按新结构(包含默认状态名)反序列化 var config = JsonConvert.DeserializeObject(jsonString, settings); if (config != null && config.blendTrees != null && config.blendTrees.Count > 0) { return config; } // 兼容旧结构:仅为 List var blendTreeInfos = JsonConvert.DeserializeObject>(jsonString, settings); if (blendTreeInfos != null) { return new BlendTreeConfig { defaultStateName = string.Empty, blendTrees = blendTreeInfos }; } Debug.LogWarning("JSON 反序列化结果为 null"); return null; } catch (Exception e) { Debug.LogError($"从 JSON 解析混合树信息失败: {e.Message}"); return null; } } /// /// 从 JSON 文件路径解析混合树信息列表 /// /// JSON 文件路径(可以是 Assets 相对路径或绝对路径) /// 混合树信息列表 public static List ParseBlendTreeFromJsonFile(string jsonFilePath) { var config = ParseBlendTreeConfigFromJsonFile(jsonFilePath); return config?.blendTrees ?? new List(); } /// /// 从 JSON 文件路径解析混合树配置(包含默认状态名和混合树列表) /// /// JSON 文件路径(可以是 Assets 相对路径或绝对路径) /// 混合树配置 public static BlendTreeConfig ParseBlendTreeConfigFromJsonFile(string jsonFilePath) { if (string.IsNullOrEmpty(jsonFilePath)) { Debug.LogWarning("JSON 文件路径为空"); return null; } try { // 处理路径 string fullPath = jsonFilePath; if (!Path.IsPathRooted(jsonFilePath)) { // 如果是相对路径,尝试转换为绝对路径 if (jsonFilePath.StartsWith("Assets/")) { fullPath = Path.Combine(Application.dataPath, jsonFilePath.Substring(7)); } else { fullPath = Path.Combine(Application.dataPath, jsonFilePath); } } // 检查文件是否存在 if (!File.Exists(fullPath)) { Debug.LogWarning($"JSON 文件不存在: {fullPath}"); return null; } // 读取文件内容 string jsonContent = File.ReadAllText(fullPath, System.Text.Encoding.UTF8); // 调用字符串解析方法 return ParseBlendTreeConfigFromJson(jsonContent); } catch (Exception e) { Debug.LogError($"从 JSON 文件解析混合树信息失败: {e.Message}"); return null; } } /// /// 从 AnimatorController 构建 shortNameHash → 状态短名 映射,供 ActorAnima 快照 Capture 反查。 /// Editor 下遍历完整状态机;运行时 fallback 为 animationClips 名称(与状态名一致时有效)。 /// public static Dictionary BuildStateHashToNameMap(RuntimeAnimatorController controller) { var map = new Dictionary(); if (controller == null) { return map; } #if UNITY_EDITOR if (controller is AnimatorController animatorController) { foreach (var layer in animatorController.layers) { if (layer.stateMachine != null) { CollectStateNamesFromStateMachine(layer.stateMachine, map); } } return map; } #endif foreach (var clip in controller.animationClips) { if (clip == null) continue; var hash = Animator.StringToHash(clip.name); map.TryAdd(hash, clip.name); } return map; } #if UNITY_EDITOR private static void CollectStateNamesFromStateMachine( AnimatorStateMachine stateMachine, Dictionary map) { if (stateMachine == null) return; foreach (var childState in stateMachine.states) { if (childState.state == null) continue; var hash = Animator.StringToHash(childState.state.name); map.TryAdd(hash, childState.state.name); } foreach (var subStateMachine in stateMachine.stateMachines) { if (subStateMachine.stateMachine != null) { CollectStateNamesFromStateMachine(subStateMachine.stateMachine, map); } } } #endif } /// /// 混合树配置(包含默认状态名和混合树列表) /// [System.Serializable] public class BlendTreeConfig { /// /// AnimatorController 的默认 State 名称 /// public string defaultStateName; /// /// 混合树信息列表 /// public List blendTrees; } /// /// 混合树信息数据结构 /// [System.Serializable] public class BlendTreeInfo { public string name; public string type; // "1D" 或 "2D" public int clipCount; // 1D 类型的动画片段数量 public List clips; // 2D 类型的动画片段列表 public float minThreshold; public float maxThreshold; public string blendParameter; public string blendParameterY; // 2D 类型使用 public BlendTreeClipInfo GetClipInfo(string clipName) { return clips.FirstOrDefault(clip => clip.clipName == clipName); } } /// /// 混合树动画片段信息(用于 1D 和 2D 类型) /// [System.Serializable] public class BlendTreeClipInfo { public string clipName; public Vector2 position; // 2D 类型使用 public float threshold; // 1D 类型使用 } }