using System; using System.Collections.Generic; using System.IO; using AibisDream.Framework; using Newtonsoft.Json; using UnityEditor; using UnityEditor.Animations; using UnityEngine; namespace AibisDream.SystemEditor { public class AnimatorBlendTreeParserWindow : EditorWindow { private AnimatorController _selectedController; private List _blendTreeInfos; private Vector2 _scrollPosition; private string _resultText = ""; private bool _includeNested = false; // 是否包含嵌套混合树,默认不提取 private string _defaultStateName = ""; // 控制器默认 State 名称 [MenuItem("Tools/Animator BlendTree Parser")] public static void ShowWindow() { GetWindow("BlendTree Parser"); } private void OnGUI() { EditorGUILayout.Space(10); // AnimatorController 选择区域 EditorGUILayout.LabelField("AnimatorController", EditorStyles.boldLabel); _selectedController = (AnimatorController)EditorGUILayout.ObjectField( "Controller", _selectedController, typeof(AnimatorController), false); EditorGUILayout.Space(10); // 解析选项 EditorGUILayout.LabelField("解析选项", EditorStyles.boldLabel); _includeNested = EditorGUILayout.Toggle("包含嵌套混合树", _includeNested); EditorGUILayout.HelpBox( _includeNested ? "将提取所有层级的混合树(包括嵌套在子混合树中的混合树)" : "仅提取第一层混合树(不包含嵌套在子混合树中的混合树)", MessageType.Info); EditorGUILayout.Space(10); // 解析按钮 EditorGUI.BeginDisabledGroup(_selectedController == null); if (GUILayout.Button("解析混合树", GUILayout.Height(30))) { ParseBlendTree(); } EditorGUI.EndDisabledGroup(); EditorGUILayout.Space(10); // 结果显示区域 if (_blendTreeInfos != null && _blendTreeInfos.Count > 0) { EditorGUILayout.LabelField("解析结果", EditorStyles.boldLabel); // 显示统计信息 EditorGUILayout.HelpBox( $"共找到 {_blendTreeInfos.Count} 个混合树", MessageType.Info); // 可滚动的文本区域 _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition, GUILayout.ExpandHeight(true)); EditorGUILayout.TextArea(_resultText, GUILayout.ExpandHeight(true)); EditorGUILayout.EndScrollView(); EditorGUILayout.Space(10); // 保存 JSON 按钮 if (GUILayout.Button("保存为 JSON", GUILayout.Height(30))) { SaveToJson(); } } else if (_blendTreeInfos != null && _blendTreeInfos.Count == 0) { EditorGUILayout.HelpBox( "未找到混合树", MessageType.Warning); } } private void ParseBlendTree() { if (_selectedController == null) { EditorUtility.DisplayDialog("错误", "请先选择 AnimatorController", "确定"); return; } try { // 解析默认 State 名称(默认使用第 0 层的默认状态) if (_selectedController.layers != null && _selectedController.layers.Length > 0) { var baseLayer = _selectedController.layers[0]; _defaultStateName = baseLayer.stateMachine?.defaultState != null ? baseLayer.stateMachine.defaultState.name : string.Empty; } else { _defaultStateName = string.Empty; } // 调用解析方法,传入 includeNested 选项 _blendTreeInfos = AnimatorKit.ParseBlendTree(_selectedController, _includeNested); // 格式化结果显示文本 FormatResultText(); Debug.Log( $"解析完成: 找到 {_blendTreeInfos.Count} 个混合树 (包含嵌套: {_includeNested}),默认 State: {_defaultStateName}"); } catch (Exception e) { EditorUtility.DisplayDialog("解析失败", e.Message, "确定"); Debug.LogError($"解析失败: {e}"); _blendTreeInfos = null; _resultText = ""; } } private void FormatResultText() { if (_blendTreeInfos == null || _blendTreeInfos.Count == 0) { _resultText = "未找到混合树"; return; } var lines = new List(); lines.Add($"=== 控制器 {_selectedController.name} 的混合树信息 ==="); lines.Add($"默认 State: {_defaultStateName}"); lines.Add($"共找到 {_blendTreeInfos.Count} 个混合树\n"); for (int i = 0; i < _blendTreeInfos.Count; i++) { var info = _blendTreeInfos[i]; lines.Add($"[{i + 1}] 混合树名称: {info.name}"); lines.Add($" 类型: {info.type}"); if (info.type == "1D") { lines.Add($" 动画片段数量: {info.clipCount}"); lines.Add($" 混合参数: {info.blendParameter}"); lines.Add($" 参数范围: [{info.minThreshold}, {info.maxThreshold}]"); lines.Add($" 动画片段列表 ({info.clips?.Count ?? 0} 个):"); if (info.clips != null) { for (int j = 0; j < info.clips.Count; j++) { var clip = info.clips[j]; lines.Add($" [{j + 1}] {clip.clipName} (Threshold: {clip.threshold})"); } } } else if (info.type == "2D") { lines.Add($" 混合参数 X: {info.blendParameter}"); lines.Add($" 混合参数 Y: {info.blendParameterY}"); lines.Add($" 参数范围: [{info.minThreshold}, {info.maxThreshold}]"); lines.Add($" 动画片段列表 ({info.clips?.Count ?? 0} 个):"); if (info.clips != null) { for (int j = 0; j < info.clips.Count; j++) { var clip = info.clips[j]; lines.Add($" [{j + 1}] {clip.clipName} (位置: {clip.position})"); } } } else { lines.Add($" 动画片段数量: {info.clipCount}"); } if (i < _blendTreeInfos.Count - 1) { lines.Add(""); } } _resultText = string.Join("\n", lines); } private void SaveToJson() { if (_selectedController == null || _blendTreeInfos == null || _blendTreeInfos.Count == 0) { EditorUtility.DisplayDialog("错误", "没有可保存的数据", "确定"); return; } try { // 获取 AnimatorController 的资产路径 string controllerPath = AssetDatabase.GetAssetPath(_selectedController); if (string.IsNullOrEmpty(controllerPath)) { EditorUtility.DisplayDialog("错误", "无法获取 AnimatorController 的路径", "确定"); return; } // 生成 JSON 文件路径(与 .controller 文件同名同路径,扩展名为 .json) string directory = Path.GetDirectoryName(controllerPath); string fileName = Path.GetFileNameWithoutExtension(controllerPath); string jsonPath = Path.Combine(directory, $"{fileName}.json"); // 转换为 Unity 资源路径格式 jsonPath = jsonPath.Replace("\\", "/"); if (!jsonPath.StartsWith("Assets/")) { // 如果不是 Assets 路径,尝试转换 string fullPath = Path.GetFullPath(jsonPath); string assetsPath = Path.GetFullPath(Application.dataPath); if (fullPath.StartsWith(assetsPath)) { jsonPath = "Assets" + fullPath.Substring(assetsPath.Length).Replace("\\", "/"); } else { EditorUtility.DisplayDialog("错误", "JSON 文件路径必须在 Assets 目录下", "确定"); return; } } // 配置序列化设置,忽略循环引用 var settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Formatting = Formatting.Indented }; // 组装配置对象(包含默认 State 名称和混合树列表) var config = new BlendTreeConfig { defaultStateName = _defaultStateName, blendTrees = _blendTreeInfos }; // 序列化为 JSON string jsonContent = JsonConvert.SerializeObject(config, settings); // 写入文件(如果已存在则覆盖) File.WriteAllText(jsonPath, jsonContent, System.Text.Encoding.UTF8); // 刷新资源数据库 AssetDatabase.Refresh(); EditorUtility.DisplayDialog("保存成功", $"JSON 文件已保存到:\n{jsonPath}", "确定"); Debug.Log($"JSON 文件已保存: {jsonPath}"); } catch (Exception e) { EditorUtility.DisplayDialog("保存失败", e.Message, "确定"); Debug.LogError($"保存 JSON 失败: {e}"); } } } }