267 lines
9.2 KiB
C#
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;
|
|
}
|
|
} |