Files
aibis-dream/Assets/Scripts/Framework/OtherKit/AnimatorKit.cs
T
2026-01-06 18:51:59 +08:00

318 lines
11 KiB
C#

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
/// <summary>
/// 解析 AnimatorController 下的所有混合树
/// </summary>
/// <param name="controller">AnimatorController 资产</param>
/// <param name="includeNested">是否包含嵌套的混合树(子混合树),默认为 true</param>
/// <returns>混合树信息列表</returns>
public static List<BlendTreeInfo> ParseBlendTree(AnimatorController controller, bool includeNested = true)
{
if (controller == null)
{
Debug.LogWarning("AnimatorController 为空");
return new List<BlendTreeInfo>();
}
var blendTreeInfos = new List<BlendTreeInfo>();
// 遍历所有层
foreach (var layer in controller.layers)
{
if (layer.stateMachine != null)
{
CollectBlendTreesFromStateMachine(layer.stateMachine, blendTreeInfos, includeNested);
}
}
return blendTreeInfos;
}
/// <summary>
/// 从状态机中收集混合树(递归处理子状态机)
/// </summary>
private static void CollectBlendTreesFromStateMachine(AnimatorStateMachine stateMachine, List<BlendTreeInfo> 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);
}
}
}
/// <summary>
/// 收集混合树信息(可选递归处理子混合树)
/// </summary>
/// <param name="blendTree">混合树</param>
/// <param name="blendTreeInfos">混合树信息列表</param>
/// <param name="includeNested">是否包含嵌套的混合树(子混合树)</param>
private static void CollectBlendTreeInfo(BlendTree blendTree, List<BlendTreeInfo> 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<BlendTreeClipInfo>();
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<BlendTreeClipInfo>();
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
/// <summary>
/// 从 JSON 字符串解析混合树配置(包含默认状态名和混合树列表)
/// </summary>
/// <param name="jsonString">JSON 字符串</param>
/// <returns>混合树配置</returns>
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<BlendTreeConfig>(jsonString, settings);
if (config != null && config.blendTrees != null && config.blendTrees.Count > 0)
{
return config;
}
// 兼容旧结构:仅为 List<BlendTreeInfo>
var blendTreeInfos = JsonConvert.DeserializeObject<List<BlendTreeInfo>>(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;
}
}
/// <summary>
/// 从 JSON 文件路径解析混合树信息列表
/// </summary>
/// <param name="jsonFilePath">JSON 文件路径(可以是 Assets 相对路径或绝对路径)</param>
/// <returns>混合树信息列表</returns>
public static List<BlendTreeInfo> ParseBlendTreeFromJsonFile(string jsonFilePath)
{
var config = ParseBlendTreeConfigFromJsonFile(jsonFilePath);
return config?.blendTrees ?? new List<BlendTreeInfo>();
}
/// <summary>
/// 从 JSON 文件路径解析混合树配置(包含默认状态名和混合树列表)
/// </summary>
/// <param name="jsonFilePath">JSON 文件路径(可以是 Assets 相对路径或绝对路径)</param>
/// <returns>混合树配置</returns>
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;
}
}
}
/// <summary>
/// 混合树配置(包含默认状态名和混合树列表)
/// </summary>
[System.Serializable]
public class BlendTreeConfig
{
/// <summary>
/// AnimatorController 的默认 State 名称
/// </summary>
public string defaultStateName;
/// <summary>
/// 混合树信息列表
/// </summary>
public List<BlendTreeInfo> blendTrees;
}
/// <summary>
/// 混合树信息数据结构
/// </summary>
[System.Serializable]
public class BlendTreeInfo
{
public string name;
public string type; // "1D" 或 "2D"
public int clipCount; // 1D 类型的动画片段数量
public List<BlendTreeClipInfo> 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);
}
}
/// <summary>
/// 混合树动画片段信息(用于 1D 和 2D 类型)
/// </summary>
[System.Serializable]
public class BlendTreeClipInfo
{
public string clipName;
public Vector2 position; // 2D 类型使用
public float threshold; // 1D 类型使用
}
}