Files
Fishing2/Assets/Scripts/Editor/AnimatorParameterAnalyzer.cs
2025-05-10 12:49:47 +08:00

267 lines
9.2 KiB
C#

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEditor.Animations;
public class AnimatorParameterDetailAnalyzer : EditorWindow
{
private AnimatorController selectedController;
private Dictionary<string, ParameterUsageData> parameterUsageData = new Dictionary<string, ParameterUsageData>();
private Vector2 scrollPosition;
private string analysisResult = "";
private bool showRawData = false;
private string targetLayerName = "";
[MenuItem("Tools/Animator Parameter Detail Analyzer")]
public static void ShowWindow()
{
GetWindow<AnimatorParameterDetailAnalyzer>("Animator Parameter Detail Analyzer");
}
private void OnGUI()
{
GUILayout.Label("Animator Parameter Detail Analyzer", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
selectedController = (AnimatorController)EditorGUILayout.ObjectField("Animator Controller", selectedController, typeof(AnimatorController), false);
if (EditorGUI.EndChangeCheck())
{
AnalyzeController();
}
targetLayerName = EditorGUILayout.TextField("Target Layer Name (Empty for all)", targetLayerName);
if (GUILayout.Button("Analyze Selected Controller"))
{
AnalyzeController();
}
showRawData = EditorGUILayout.Toggle("Show Raw Data", showRawData);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
EditorGUILayout.TextArea(analysisResult, GUILayout.ExpandHeight(true));
EditorGUILayout.EndScrollView();
}
private void AnalyzeController()
{
parameterUsageData.Clear();
analysisResult = "";
if (selectedController == null)
{
Debug.LogWarning("No Animator Controller selected.");
return;
}
StringBuilder sb = new StringBuilder();
sb.AppendLine($"=== Animator Controller Analysis: {selectedController.name} ===");
if (string.IsNullOrEmpty(targetLayerName))
{
sb.AppendLine("Analyzing ALL layers");
}
else
{
sb.AppendLine($"Analyzing layer: {targetLayerName}");
}
sb.AppendLine();
foreach (var param in selectedController.parameters)
{
parameterUsageData[param.name] = new ParameterUsageData(param);
}
foreach (var layer in selectedController.layers)
{
if (string.IsNullOrEmpty(targetLayerName) || layer.name == targetLayerName)
{
AnalyzeStateMachine(layer.stateMachine, layer.name);
}
}
foreach (var paramData in parameterUsageData.Values.OrderBy(d => d.parameter.name))
{
AppendParameterReport(sb, paramData);
}
if (showRawData)
{
sb.AppendLine();
sb.AppendLine("=== Raw Transition Data ===");
foreach (var layer in selectedController.layers)
{
if (string.IsNullOrEmpty(targetLayerName) || layer.name == targetLayerName)
{
AppendRawTransitionData(sb, layer.stateMachine, layer.name);
}
}
}
analysisResult = sb.ToString();
}
private void AppendParameterReport(StringBuilder sb, ParameterUsageData paramData)
{
var param = paramData.parameter;
sb.AppendLine($"Parameter: {param.name} ({param.type})");
sb.Append(" Possible Values: ");
switch (param.type)
{
case AnimatorControllerParameterType.Int:
sb.AppendLine(string.Join(", ", paramData.possibleIntValues.OrderBy(v => v).Select(v => v.ToString())));
break;
case AnimatorControllerParameterType.Float:
sb.AppendLine(string.Join(", ", paramData.possibleFloatValues.OrderBy(v => v).Select(v => v.ToString("F2"))));
break;
case AnimatorControllerParameterType.Bool:
sb.AppendLine(string.Join(", ", paramData.possibleBoolValues.Select(v => v.ToString())));
break;
case AnimatorControllerParameterType.Trigger:
sb.AppendLine("Trigger (used in conditions)");
break;
}
if (paramData.transitionUsages.Count > 0)
{
sb.AppendLine(" Used in Transitions:");
foreach (var usage in paramData.transitionUsages)
{
sb.AppendLine($" {usage.fromState} -> {usage.toState} (Layer: {usage.layerName})");
sb.AppendLine($" Condition: {param.name} {GetConditionSymbol(usage.condition.mode)} {usage.condition.threshold}");
}
}
else
{
sb.AppendLine(" Not used in any transitions (may be used in scripts)");
}
sb.AppendLine();
}
private string GetConditionSymbol(AnimatorConditionMode mode)
{
switch (mode)
{
case AnimatorConditionMode.If: return "==";
case AnimatorConditionMode.IfNot: return "!=";
case AnimatorConditionMode.Greater: return ">";
case AnimatorConditionMode.Less: return "<";
case AnimatorConditionMode.Equals: return "==";
case AnimatorConditionMode.NotEqual: return "!=";
default: return "?";
}
}
private void AnalyzeStateMachine(AnimatorStateMachine stateMachine, string layerName)
{
foreach (var childState in stateMachine.states)
{
AnalyzeState(childState.state, layerName);
}
foreach (var childStateMachine in stateMachine.stateMachines)
{
AnalyzeStateMachine(childStateMachine.stateMachine, layerName);
}
}
private void AnalyzeState(AnimatorState state, string layerName)
{
foreach (var transition in state.transitions)
{
AnalyzeTransition(transition, state.name, layerName);
}
}
private void AnalyzeTransition(AnimatorStateTransition transition, string fromStateName, string layerName)
{
foreach (var condition in transition.conditions)
{
if (!parameterUsageData.ContainsKey(condition.parameter))
continue;
var paramData = parameterUsageData[condition.parameter];
var usage = new TransitionUsage
{
layerName = layerName,
fromState = fromStateName,
toState = transition.destinationState?.name ?? "Exit",
condition = condition
};
paramData.transitionUsages.Add(usage);
switch (paramData.parameter.type)
{
case AnimatorControllerParameterType.Int:
paramData.possibleIntValues.Add((int)condition.threshold);
break;
case AnimatorControllerParameterType.Float:
paramData.possibleFloatValues.Add(condition.threshold);
break;
case AnimatorControllerParameterType.Bool:
paramData.possibleBoolValues.Add(condition.threshold > 0.5f);
break;
}
}
}
private void AppendRawTransitionData(StringBuilder sb, AnimatorStateMachine stateMachine, string layerName)
{
foreach (var childState in stateMachine.states)
{
foreach (var transition in childState.state.transitions)
{
sb.AppendLine($"{layerName}: {childState.state.name} -> {transition.destinationState?.name ?? "Exit"}");
foreach (var condition in transition.conditions)
{
sb.AppendLine($" {condition.parameter} {condition.mode} {condition.threshold}");
}
}
}
foreach (var childStateMachine in stateMachine.stateMachines)
{
AppendRawTransitionData(sb, childStateMachine.stateMachine, layerName);
}
}
private class ParameterUsageData
{
public AnimatorControllerParameter parameter;
public HashSet<int> possibleIntValues = new HashSet<int>();
public HashSet<float> possibleFloatValues = new HashSet<float>();
public HashSet<bool> possibleBoolValues = new HashSet<bool>();
public List<TransitionUsage> transitionUsages = new List<TransitionUsage>();
public ParameterUsageData(AnimatorControllerParameter param)
{
parameter = param;
switch (param.type)
{
case AnimatorControllerParameterType.Int:
possibleIntValues.Add(param.defaultInt);
break;
case AnimatorControllerParameterType.Float:
possibleFloatValues.Add(param.defaultFloat);
break;
case AnimatorControllerParameterType.Bool:
possibleBoolValues.Add(param.defaultBool);
break;
}
}
}
private class TransitionUsage
{
public string layerName;
public string fromState;
public string toState;
public AnimatorCondition condition;
}
}