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
|
||
|