Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Adds state and parameter selectors.
//=============================================================================
// Licensed under CC0. Takanori Shibasaki
//=============================================================================
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
[System.Serializable]
public struct AnimatorStateName
{
[SerializeField]
internal int m_Hash;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator int(AnimatorStateName name)
{
return name.m_Hash;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator bool(AnimatorStateName name)
{
return name.m_Hash != 0;
}
}
[System.Serializable]
public struct AnimatorParameterName
{
[SerializeField]
internal int m_Hash;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator int(AnimatorParameterName name)
{
return name.m_Hash;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator bool(AnimatorParameterName name)
{
return name.m_Hash != 0;
}
}
#if UNITY_EDITOR
abstract class AnimatorPropertyDrawer : UnityEditor.PropertyDrawer
{
class AnimatorList : List<Animator>
{
public bool Created;
}
protected static List<Animator> GetAnimators(UnityEditor.SerializedProperty property)
{
var controlID = GUIUtility.GetControlID(FocusType.Passive);
var animatorList = GUIUtility.GetStateObject(typeof(AnimatorList), controlID) as AnimatorList;
if (animatorList.Created)
{
return animatorList;
}
foreach (var obj in property.serializedObject.targetObjects)
{
if (obj is Component component)
{
var animator = component.GetComponent<Animator>();
if (animator != null)
{
animatorList.Add(animator);
continue;
}
}
animatorList.Clear();
break;
}
animatorList.Created = true;
return animatorList;
}
}
[UnityEditor.CustomPropertyDrawer(typeof(AnimatorStateName))]
class AnimatorStateNameDrawer : AnimatorPropertyDrawer
{
static SortedList<string, int> m_StateNames = new SortedList<string, int>();
static List<string> m_Options = new List<string>();
static List<int> m_Values = new List<int>();
static void GetStateNames(List<Animator> animators, SortedList<string, int> result)
{
static void GetStateNames(UnityEditor.Animations.AnimatorStateMachine stateMachine, SortedList<string, int> result)
{
foreach (var childState in stateMachine.states)
{
var hash = Animator.StringToHash(childState.state.name);
result[childState.state.name] = hash;
}
foreach (var childStateMachine in stateMachine.stateMachines)
{
if (!string.IsNullOrEmpty(childStateMachine.stateMachine.name))
{
var hash = Animator.StringToHash(childStateMachine.stateMachine.name);
result[childStateMachine.stateMachine.name] = hash;
}
GetStateNames(childStateMachine.stateMachine, result);
}
}
result.Clear();
foreach (var animator in animators)
{
if (animator.runtimeAnimatorController is UnityEditor.Animations.AnimatorController controller)
{
foreach (var layer in controller.layers)
{
GetStateNames(layer.stateMachine, m_StateNames);
}
}
}
}
public override void OnGUI(Rect position, UnityEditor.SerializedProperty property, GUIContent label)
{
var animators = GetAnimators(property);
if (animators.Count <= 0)
{
return;
}
GetStateNames(animators, m_StateNames);
m_Options.Clear();
m_Values.Clear();
m_Options.Add("[None]");
m_Values.Add(0);
foreach (var pair in m_StateNames)
{
m_Options.Add(pair.Key);
m_Values.Add(pair.Value);
}
var selection = -1;
var hashProperty = property.FindPropertyRelative(nameof(AnimatorStateName.m_Hash));
UnityEditor.EditorGUI.showMixedValue = property.hasMultipleDifferentValues;
if (!UnityEditor.EditorGUI.showMixedValue)
{
if (hashProperty.intValue == 0)
{
selection = 0;
}
else
{
selection = m_StateNames.IndexOfValue(hashProperty.intValue) + 1;
if (selection == 0)
{
selection = m_Options.Count;
m_Options.Add($"Unknown State {hashProperty.intValue}");
m_Values.Add(property.intValue);
}
}
}
UnityEditor.EditorGUI.BeginChangeCheck();
selection = UnityEditor.EditorGUI.Popup(position, label.text, selection, m_Options.ToArray());
if (UnityEditor.EditorGUI.EndChangeCheck())
{
hashProperty.intValue = m_Values[selection];
}
}
public override float GetPropertyHeight(UnityEditor.SerializedProperty property, GUIContent label)
{
var animators = GetAnimators(property);
return 0 < animators.Count ? base.GetPropertyHeight(property, label) : 0;
}
}
[UnityEditor.CustomPropertyDrawer(typeof(AnimatorParameterName))]
class AnimatorParameterNameDrawer : AnimatorPropertyDrawer
{
static SortedList<string, int> m_ParameterNames = new SortedList<string, int>();
static List<string> m_Options = new List<string>();
static List<int> m_Values = new List<int>();
static void GetParameterNames(List<Animator> animators, SortedList<string, int> result)
{
result.Clear();
foreach (var animator in animators)
{
if (animator.runtimeAnimatorController is UnityEditor.Animations.AnimatorController controller)
{
foreach (var parameter in controller.parameters)
{
result[parameter.name] = parameter.nameHash;
}
}
}
}
public override void OnGUI(Rect position, UnityEditor.SerializedProperty property, GUIContent label)
{
var animators = GetAnimators(property);
if (animators.Count <= 0)
{
return;
}
GetParameterNames(animators, m_ParameterNames);
m_Options.Clear();
m_Values.Clear();
m_Options.Add("[None]");
m_Values.Add(0);
foreach (var pair in m_ParameterNames)
{
m_Options.Add(pair.Key);
m_Values.Add(pair.Value);
}
var selection = -1;
var hashProperty = property.FindPropertyRelative(nameof(AnimatorStateName.m_Hash));
UnityEditor.EditorGUI.showMixedValue = property.hasMultipleDifferentValues;
if (!UnityEditor.EditorGUI.showMixedValue)
{
if (hashProperty.intValue == 0)
{
selection = 0;
}
else
{
selection = m_ParameterNames.IndexOfValue(hashProperty.intValue) + 1;
if (selection == 0)
{
selection = m_Options.Count;
m_Options.Add($"Unknown Parameter {hashProperty.intValue}");
m_Values.Add(property.intValue);
}
}
}
UnityEditor.EditorGUI.BeginChangeCheck();
selection = UnityEditor.EditorGUI.Popup(position, label.text, selection, m_Options.ToArray());
if (UnityEditor.EditorGUI.EndChangeCheck())
{
hashProperty.intValue = m_Values[selection];
}
}
public override float GetPropertyHeight(UnityEditor.SerializedProperty property, GUIContent label)
{
var animators = GetAnimators(property);
return 0 < animators.Count ? base.GetPropertyHeight(property, label) : 0;
}
}
#endif // UNITY_EDITOR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment