277 lines
9.7 KiB
C#
277 lines
9.7 KiB
C#
![]() |
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
|
|||
|
using System.Collections.Generic;
|
|||
|
using UnityEditor;
|
|||
|
using UnityEditor.Animations;
|
|||
|
using UnityEngine;
|
|||
|
using static Animancer.Editor.AnimancerGUI;
|
|||
|
|
|||
|
namespace Animancer.Editor
|
|||
|
{
|
|||
|
/// <summary><see cref="PropertyDrawer"/> for <see cref="ControllerState.SerializableParameterBindings"/>.</summary>
|
|||
|
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/SerializableParameterBindingsDrawer
|
|||
|
[CustomPropertyDrawer(typeof(ControllerState.SerializableParameterBindings), true)]
|
|||
|
public class SerializableParameterBindingsDrawer : PropertyDrawer
|
|||
|
{
|
|||
|
/************************************************************************************************************************/
|
|||
|
|
|||
|
/// <inheritdoc/>
|
|||
|
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
|
|||
|
{
|
|||
|
if (!property.isExpanded)
|
|||
|
return LineHeight;
|
|||
|
|
|||
|
GetFields(property, out var mode, out var bindings);
|
|||
|
|
|||
|
var count = bindings.arraySize;
|
|||
|
if (count > 0 && mode.boolValue)
|
|||
|
count = 1 + Mathf.CeilToInt(count * 0.5f);
|
|||
|
|
|||
|
return CalculateHeight(count + 3);
|
|||
|
}
|
|||
|
|
|||
|
/************************************************************************************************************************/
|
|||
|
|
|||
|
/// <inheritdoc/>
|
|||
|
public override void OnGUI(Rect area, SerializedProperty property, GUIContent label)
|
|||
|
{
|
|||
|
area.height = LineHeight;
|
|||
|
|
|||
|
var isExpanded = EditorGUI.PropertyField(area, property, label, false);
|
|||
|
if (!isExpanded)
|
|||
|
return;
|
|||
|
|
|||
|
EditorGUI.indentLevel++;
|
|||
|
|
|||
|
NextVerticalArea(ref area);
|
|||
|
|
|||
|
GetFields(property, out var mode, out var bindings);
|
|||
|
|
|||
|
var parameterList = GetContextParameterList(property);
|
|||
|
|
|||
|
var bindingCount = bindings.arraySize;
|
|||
|
|
|||
|
DoModeGUI(ref area, mode, bindingCount, parameterList);
|
|||
|
|
|||
|
var modeValue = mode.boolValue;
|
|||
|
|
|||
|
DoBindingCountGUI(ref area, bindings, modeValue, ref bindingCount, parameterList);
|
|||
|
|
|||
|
DoBindingsGUI(area, bindings, modeValue, bindingCount, parameterList);
|
|||
|
|
|||
|
EditorGUI.indentLevel--;
|
|||
|
}
|
|||
|
|
|||
|
/************************************************************************************************************************/
|
|||
|
|
|||
|
private void DoModeGUI(
|
|||
|
ref Rect area,
|
|||
|
SerializedProperty mode,
|
|||
|
int bindingCount,
|
|||
|
string parameterList)
|
|||
|
{
|
|||
|
using var label = PooledGUIContent.Acquire();
|
|||
|
|
|||
|
if (bindingCount == 0)
|
|||
|
{
|
|||
|
label.text = "Bind All Parameters";
|
|||
|
label.tooltip =
|
|||
|
"If enabled, all parameters in the Animator Controller will be bound" +
|
|||
|
" to Animancer parameters with the same name and the Bindings array can be left empty." +
|
|||
|
parameterList;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
label.text = "Rebind Names";
|
|||
|
label.tooltip =
|
|||
|
"If enabled, the Bindings array will be taken in pairs so that each" +
|
|||
|
" Animator Controller parameter can be bound to an Animancer Parameter with different name." +
|
|||
|
parameterList;
|
|||
|
}
|
|||
|
|
|||
|
EditorGUI.PropertyField(area, mode, label, false);
|
|||
|
|
|||
|
NextVerticalArea(ref area);
|
|||
|
}
|
|||
|
|
|||
|
/************************************************************************************************************************/
|
|||
|
|
|||
|
private void DoBindingCountGUI(
|
|||
|
ref Rect area,
|
|||
|
SerializedProperty bindings,
|
|||
|
bool mode,
|
|||
|
ref int bindingCount,
|
|||
|
string parameterList)
|
|||
|
{
|
|||
|
using var label = PooledGUIContent.Acquire(
|
|||
|
"Bindings",
|
|||
|
"The names of parameters in the Animator Controller to bind to Animancer parameters." +
|
|||
|
"\n<> Leave this array empty and enable the toggle if you want to bind all parameters." +
|
|||
|
parameterList);
|
|||
|
|
|||
|
var newCount = bindingCount;
|
|||
|
|
|||
|
if (mode && bindingCount > 0)
|
|||
|
newCount /= 2;
|
|||
|
|
|||
|
newCount = EditorGUI.DelayedIntField(area, label, newCount);
|
|||
|
|
|||
|
if (newCount < 0)
|
|||
|
newCount = 0;
|
|||
|
else if (mode && newCount > 0)
|
|||
|
newCount *= 2;
|
|||
|
|
|||
|
if (bindingCount != newCount)
|
|||
|
{
|
|||
|
bindingCount = newCount;
|
|||
|
bindings.arraySize = newCount;
|
|||
|
}
|
|||
|
|
|||
|
NextVerticalArea(ref area);
|
|||
|
}
|
|||
|
|
|||
|
/************************************************************************************************************************/
|
|||
|
|
|||
|
private void DoBindingsGUI(
|
|||
|
Rect area,
|
|||
|
SerializedProperty bindings,
|
|||
|
bool mode,
|
|||
|
int bindingCount,
|
|||
|
string parameterList)
|
|||
|
{
|
|||
|
if (bindingCount <= 0)
|
|||
|
return;
|
|||
|
|
|||
|
using var label = PooledGUIContent.Acquire();
|
|||
|
|
|||
|
if (mode)
|
|||
|
{
|
|||
|
var controllerArea = EditorGUI.IndentedRect(area);
|
|||
|
controllerArea.xMin -= 1;// Not sure why.
|
|||
|
var animancerArea = StealFromRight(ref controllerArea, controllerArea.width * 0.5f, StandardSpacing);
|
|||
|
|
|||
|
label.text = "Controller";
|
|||
|
label.tooltip = "The name of the Animator Controller parameter" + parameterList;
|
|||
|
GUI.Label(controllerArea, label);
|
|||
|
|
|||
|
label.text = "Animancer";
|
|||
|
label.tooltip = "The name of the Animancer parameter";
|
|||
|
GUI.Label(animancerArea, label);
|
|||
|
|
|||
|
NextVerticalArea(ref controllerArea);
|
|||
|
NextVerticalArea(ref animancerArea);
|
|||
|
|
|||
|
for (int i = 0; i < bindingCount; i++)
|
|||
|
{
|
|||
|
DoBindingGUI(ref controllerArea, bindings, i, GUIContent.none);
|
|||
|
i++;
|
|||
|
DoBindingGUI(ref animancerArea, bindings, i, GUIContent.none);
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
EditorGUI.indentLevel++;
|
|||
|
|
|||
|
label.tooltip = "";
|
|||
|
|
|||
|
for (int i = 0; i < bindingCount; i++)
|
|||
|
{
|
|||
|
label.text = "Binding " + i;
|
|||
|
DoBindingGUI(ref area, bindings, i, label);
|
|||
|
}
|
|||
|
|
|||
|
EditorGUI.indentLevel--;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/************************************************************************************************************************/
|
|||
|
|
|||
|
private static void DoBindingGUI(ref Rect area, SerializedProperty bindings, int index, GUIContent label)
|
|||
|
{
|
|||
|
var indentLevel = EditorGUI.indentLevel;
|
|||
|
if (string.IsNullOrEmpty(label.text))
|
|||
|
EditorGUI.indentLevel = 0;
|
|||
|
|
|||
|
var binding = bindings.GetArrayElementAtIndex(index);
|
|||
|
|
|||
|
EditorGUI.PropertyField(area, binding, label);
|
|||
|
|
|||
|
NextVerticalArea(ref area);
|
|||
|
|
|||
|
EditorGUI.indentLevel = indentLevel;
|
|||
|
}
|
|||
|
|
|||
|
/************************************************************************************************************************/
|
|||
|
|
|||
|
private void GetFields(
|
|||
|
SerializedProperty root,
|
|||
|
out SerializedProperty mode,
|
|||
|
out SerializedProperty bindings)
|
|||
|
{
|
|||
|
mode = root.FindPropertyRelative(ControllerState.SerializableParameterBindings.ModeFieldName);
|
|||
|
bindings = root.FindPropertyRelative(ControllerState.SerializableParameterBindings.BindingsFieldName);
|
|||
|
}
|
|||
|
|
|||
|
/************************************************************************************************************************/
|
|||
|
|
|||
|
private string GetContextParameterList(SerializedProperty property)
|
|||
|
{
|
|||
|
var path = property.propertyPath;
|
|||
|
var lastDot = path.LastIndexOf('.');
|
|||
|
if (lastDot < 0)
|
|||
|
return null;
|
|||
|
|
|||
|
path = path[..(lastDot + 1)] + ControllerTransition.ControllerFieldName;
|
|||
|
property = property.serializedObject.FindProperty(path);
|
|||
|
if (property == null ||
|
|||
|
property.objectReferenceValue is not AnimatorController animatorController)
|
|||
|
return null;
|
|||
|
|
|||
|
return GetParameterList(animatorController);
|
|||
|
}
|
|||
|
|
|||
|
private readonly Dictionary<AnimatorController, string>
|
|||
|
ControllerToParameterList = new();
|
|||
|
|
|||
|
private string GetParameterList(AnimatorController animatorController)
|
|||
|
{
|
|||
|
if (animatorController == null)
|
|||
|
return null;
|
|||
|
|
|||
|
if (ControllerToParameterList.TryGetValue(animatorController, out var parameterList))
|
|||
|
return parameterList;
|
|||
|
|
|||
|
var text = StringBuilderPool.Instance.Acquire();
|
|||
|
|
|||
|
var parameters = animatorController.parameters;
|
|||
|
if (parameters.Length > 0)
|
|||
|
{
|
|||
|
text.Append("\n\nParameters in ")
|
|||
|
.Append(animatorController.name)
|
|||
|
.Append(':');
|
|||
|
|
|||
|
for (int i = 0; i < parameters.Length; i++)
|
|||
|
{
|
|||
|
var parameter = parameters[i];
|
|||
|
|
|||
|
text.Append("\n<> ")
|
|||
|
.Append(parameter.type)
|
|||
|
.Append(' ')
|
|||
|
.Append(parameter.name);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
parameterList = text.ReleaseToString();
|
|||
|
ControllerToParameterList.Add(animatorController, parameterList);
|
|||
|
return parameterList;
|
|||
|
}
|
|||
|
|
|||
|
/************************************************************************************************************************/
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endif
|
|||
|
|