首次提交
This commit is contained in:
267
Assets/Scripts/Editor/AnimatorParameterAnalyzer.cs
Normal file
267
Assets/Scripts/Editor/AnimatorParameterAnalyzer.cs
Normal file
@@ -0,0 +1,267 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user