384 lines
No EOL
15 KiB
C#
Executable file
384 lines
No EOL
15 KiB
C#
Executable file
using System;
|
|
using System.Collections.Generic;
|
|
using ABI.CCK.Components;
|
|
using ABI.CCK.Scripts.Editor;
|
|
using UnityEditor;
|
|
using UnityEditorInternal;
|
|
using UnityEngine;
|
|
using AnimatorController = UnityEditor.Animations.AnimatorController;
|
|
using AnimatorControllerParameter = UnityEngine.AnimatorControllerParameter;
|
|
using AnimatorControllerParameterType = UnityEngine.AnimatorControllerParameterType;
|
|
|
|
[CustomEditor(typeof(AnimatorDriver))]
|
|
public class CCK_AnimatorDriverEditor : Editor
|
|
{
|
|
private AnimatorDriver _animatorDriver;
|
|
|
|
private ReorderableList _onEnterList;
|
|
private ReorderableList _onExitList;
|
|
|
|
private readonly List<string> _animatorParamNames = new List<string>();
|
|
private readonly List<AnimatorDriverTask.ParameterType> _animatorParamTypes = new List<AnimatorDriverTask.ParameterType>();
|
|
|
|
private bool _showLocalOnlyHelp;
|
|
private bool _isInPlayMode;
|
|
|
|
#region Unity Events
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (target == null) return;
|
|
_animatorDriver = (AnimatorDriver)target;
|
|
|
|
_isInPlayMode = Application.isPlaying;
|
|
|
|
_animatorParamNames.Clear();
|
|
_animatorParamTypes.Clear();
|
|
// TODO: add parameter type display in parameter dropdown
|
|
|
|
var behaviorContext = AnimatorController.FindStateMachineBehaviourContext(_animatorDriver);
|
|
if (behaviorContext.Length > 0)
|
|
{
|
|
AnimatorController controller = behaviorContext[0].animatorController;
|
|
foreach (AnimatorControllerParameter parameter in controller.parameters)
|
|
{
|
|
switch (parameter.type)
|
|
{
|
|
case AnimatorControllerParameterType.Bool:
|
|
_animatorParamNames.Add($"{parameter.name}");
|
|
_animatorParamTypes.Add(AnimatorDriverTask.ParameterType.Bool);
|
|
break;
|
|
case AnimatorControllerParameterType.Float:
|
|
_animatorParamNames.Add($"{parameter.name}");
|
|
_animatorParamTypes.Add(AnimatorDriverTask.ParameterType.Float);
|
|
break;
|
|
case AnimatorControllerParameterType.Int:
|
|
_animatorParamNames.Add($"{parameter.name}");
|
|
_animatorParamTypes.Add(AnimatorDriverTask.ParameterType.Int);
|
|
break;
|
|
case AnimatorControllerParameterType.Trigger:
|
|
_animatorParamNames.Add($"{parameter.name}");
|
|
_animatorParamTypes.Add(AnimatorDriverTask.ParameterType.Trigger);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_onEnterList == null)
|
|
{
|
|
_onEnterList = new ReorderableList(_animatorDriver.EnterTasks, typeof(AnimatorDriverTask),
|
|
true, true, !_isInPlayMode, !_isInPlayMode)
|
|
{
|
|
drawHeaderCallback = OnDrawHeaderTaskEnter,
|
|
drawElementCallback = OnDrawElementTaskEnter,
|
|
elementHeightCallback = OnHeightElementTaskEnter
|
|
};
|
|
}
|
|
|
|
if (_onExitList == null)
|
|
{
|
|
_onExitList = new ReorderableList(_animatorDriver.ExitTasks, typeof(AnimatorDriverTask),
|
|
true, true, !_isInPlayMode, !_isInPlayMode)
|
|
{
|
|
drawHeaderCallback = OnDrawHeaderTaskExit,
|
|
drawElementCallback = OnDrawElementTaskExit,
|
|
elementHeightCallback = OnHeightElementTaskExit
|
|
};
|
|
}
|
|
}
|
|
|
|
public override void OnInspectorGUI()
|
|
{
|
|
if (_animatorDriver == null)
|
|
return;
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
using (new EditorGUILayout.VerticalScope(new GUIStyle() { padding = new RectOffset(10, 10, 10, 10) }))
|
|
{
|
|
DrawLocalOnlyToggle();
|
|
DrawTaskLists();
|
|
}
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
EditorUtility.SetDirty(_animatorDriver);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GUI Drawing
|
|
|
|
private void DrawLocalOnlyToggle()
|
|
{
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
_animatorDriver.localOnly = EditorGUILayout.Toggle("Local Only", _animatorDriver.localOnly);
|
|
if (GUILayout.Button("?", GUILayout.Width(25)))
|
|
_showLocalOnlyHelp = !_showLocalOnlyHelp;
|
|
GUILayout.Space(5);
|
|
}
|
|
if (_showLocalOnlyHelp)
|
|
{
|
|
// TODO: Add to LocalizationProvider
|
|
EditorGUILayout.HelpBox("When 'Local Only' is enabled, the animator driver is executed locally and not for remote users.", MessageType.Info);
|
|
EditorGUILayout.Space(5);
|
|
EditorGUILayout.HelpBox("Avatars: Only the wearer.\nSpawnables: Only the prop's owner.\nWorlds: This option is ignored.", MessageType.Info);
|
|
}
|
|
EditorGUILayout.Space();
|
|
}
|
|
|
|
private void DrawTaskLists()
|
|
{
|
|
EditorGUILayout.Space();
|
|
_onEnterList.DoLayoutList();
|
|
|
|
EditorGUILayout.Space();
|
|
_onExitList.DoLayoutList();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ReorderableList Drawing
|
|
|
|
private void OnDrawHeaderTaskEnter(Rect rect)
|
|
{
|
|
Rect labelRect = new Rect(rect.x, rect.y, rect.width - 35, EditorGUIUtility.singleLineHeight);
|
|
GUI.Label(labelRect, "On Enter Tasks");
|
|
EditorGUIExtensions.UtilityMenu(rect, _onEnterList,
|
|
appendAdditionalMenuItems: (menuBuilder, list) => {
|
|
AppendComponentMenu(menuBuilder, list, true);
|
|
});
|
|
}
|
|
|
|
private void OnDrawElementTaskEnter(Rect rect, int index, bool isactive, bool isfocused)
|
|
{
|
|
if (index >= _animatorDriver.EnterTasks.Count) return;
|
|
AnimatorDriverTask element = _animatorDriver.EnterTasks[index];
|
|
using (new EditorGUI.DisabledScope(_isInPlayMode))
|
|
RenderTask(rect, element);
|
|
}
|
|
|
|
private float OnHeightElementTaskEnter(int index)
|
|
{
|
|
const int length = 3;
|
|
if (index >= _animatorDriver.EnterTasks.Count)
|
|
return 1.25f * length * EditorGUIUtility.singleLineHeight;
|
|
|
|
AnimatorDriverTask task = _animatorDriver.EnterTasks[index];
|
|
return (length + TaskHeight(task)) * 1.25f * EditorGUIUtility.singleLineHeight;
|
|
}
|
|
|
|
private void OnDrawHeaderTaskExit(Rect rect)
|
|
{
|
|
Rect labelRect = new Rect(rect.x, rect.y, rect.width - 35, EditorGUIUtility.singleLineHeight);
|
|
GUI.Label(labelRect, "On Exit Tasks");
|
|
EditorGUIExtensions.UtilityMenu(rect, _onExitList,
|
|
appendAdditionalMenuItems: (menuBuilder, list) => {
|
|
AppendComponentMenu(menuBuilder, list, false);
|
|
});
|
|
}
|
|
|
|
private void OnDrawElementTaskExit(Rect rect, int index, bool isactive, bool isfocused)
|
|
{
|
|
if (index >= _animatorDriver.ExitTasks.Count) return;
|
|
AnimatorDriverTask element = _animatorDriver.ExitTasks[index];
|
|
using (new EditorGUI.DisabledScope(_isInPlayMode))
|
|
RenderTask(rect, element);
|
|
}
|
|
|
|
private float OnHeightElementTaskExit(int index)
|
|
{
|
|
const int length = 3;
|
|
if (index >= _animatorDriver.ExitTasks.Count)
|
|
return 1.25f * length * EditorGUIUtility.singleLineHeight;
|
|
|
|
AnimatorDriverTask task = _animatorDriver.ExitTasks[index];
|
|
return (length + TaskHeight(task)) * 1.25f * EditorGUIUtility.singleLineHeight;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region AnimatorDriverTask Drawing
|
|
|
|
private int TaskHeight(AnimatorDriverTask task)
|
|
{
|
|
int length = 2;
|
|
if (task.aType == AnimatorDriverTask.SourceType.Random)
|
|
length += 1;
|
|
|
|
if (task.op == AnimatorDriverTask.Operator.Set)
|
|
return length;
|
|
|
|
length += task.bType == AnimatorDriverTask.SourceType.Random ? 3 : 2;
|
|
return length;
|
|
}
|
|
|
|
private void RenderTask(Rect rect, AnimatorDriverTask task)
|
|
{
|
|
Rect _rect = new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight);
|
|
|
|
float spacing = EditorGUIUtility.singleLineHeight * 1.25f;
|
|
float originalLabelWidth = EditorGUIUtility.labelWidth;
|
|
EditorGUIUtility.labelWidth = 100;
|
|
|
|
task.targetName = EditorGUIExtensions.AdvancedDropdownInput(_rect, task.targetName, _animatorParamNames,
|
|
"Parameter", "No Parameters");
|
|
_rect.y += spacing;
|
|
|
|
var formulaDisplay = $"{task.targetName} = ";
|
|
int parameterIndex = Math.Max(_animatorParamNames.FindIndex(m => m == task.targetName), 0);
|
|
if (_animatorParamNames.Count != 0 && _animatorParamTypes.Count >= parameterIndex)
|
|
task.targetType = _animatorParamTypes[parameterIndex];
|
|
|
|
task.op = (AnimatorDriverTask.Operator)EditorGUI.EnumPopup(_rect, "Operation", task.op);
|
|
_rect.y += spacing;
|
|
|
|
task.aType = (AnimatorDriverTask.SourceType)EditorGUI.EnumPopup(_rect, "A Type", task.aType);
|
|
_rect.y += spacing;
|
|
|
|
switch (task.aType)
|
|
{
|
|
case AnimatorDriverTask.SourceType.Static:
|
|
task.aValue = EditorGUI.FloatField(_rect, "A Value", task.aValue);
|
|
_rect.y += spacing;
|
|
formulaDisplay += $"{task.aValue} ";
|
|
break;
|
|
case AnimatorDriverTask.SourceType.Parameter:
|
|
task.aName = EditorGUIExtensions.AdvancedDropdownInput(_rect, task.aName, _animatorParamNames,
|
|
"Parameter A", "No Parameters");
|
|
_rect.y += spacing;
|
|
parameterIndex = Math.Max(_animatorParamNames.FindIndex(m => m == task.aName), 0);
|
|
task.aParamType = _animatorParamTypes[parameterIndex];
|
|
formulaDisplay += $"{task.aName} ";
|
|
break;
|
|
case AnimatorDriverTask.SourceType.Random:
|
|
task.aValue = EditorGUI.FloatField(_rect, "A Min", task.aValue);
|
|
_rect.y += spacing;
|
|
task.aMax = EditorGUI.FloatField(_rect, "A Max", task.aMax);
|
|
_rect.y += spacing;
|
|
formulaDisplay += $"Rand({task.aValue}, {task.aMax}) ";
|
|
break;
|
|
}
|
|
|
|
if (task.op != AnimatorDriverTask.Operator.Set)
|
|
{
|
|
switch (task.op)
|
|
{
|
|
case AnimatorDriverTask.Operator.Addition:
|
|
formulaDisplay += "+ ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.Subtraction:
|
|
formulaDisplay += "- ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.Multiplication:
|
|
formulaDisplay += "* ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.Division:
|
|
formulaDisplay += "/ ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.Modulo:
|
|
formulaDisplay += "% ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.Power:
|
|
formulaDisplay += "pow ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.Log:
|
|
formulaDisplay += "log ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.Equal:
|
|
formulaDisplay += "== ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.LessThen:
|
|
formulaDisplay += "< ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.LessEqual:
|
|
formulaDisplay += "<= ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.MoreThen:
|
|
formulaDisplay += "> ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.MoreEqual:
|
|
formulaDisplay += ">= ";
|
|
break;
|
|
case AnimatorDriverTask.Operator.NotEqual:
|
|
formulaDisplay += "!= ";
|
|
break;
|
|
}
|
|
|
|
task.bType = (AnimatorDriverTask.SourceType) EditorGUI.EnumPopup(_rect, "B Type", task.bType);
|
|
_rect.y += spacing;
|
|
|
|
switch (task.bType)
|
|
{
|
|
case AnimatorDriverTask.SourceType.Static:
|
|
task.bValue = EditorGUI.FloatField(_rect, "B Value", task.bValue);
|
|
_rect.y += spacing;
|
|
formulaDisplay += $"{task.bValue} ";
|
|
break;
|
|
case AnimatorDriverTask.SourceType.Parameter:
|
|
task.bName = EditorGUIExtensions.AdvancedDropdownInput(_rect, task.bName, _animatorParamNames,
|
|
"Parameter B", "No Parameters");
|
|
_rect.y += spacing;
|
|
parameterIndex = Math.Max(_animatorParamNames.FindIndex(m => m == task.bName), 0);
|
|
task.bParamType = _animatorParamTypes[parameterIndex];
|
|
formulaDisplay += $"{task.bName} ";
|
|
break;
|
|
case AnimatorDriverTask.SourceType.Random:
|
|
task.bValue = EditorGUI.FloatField(_rect, "B Min", task.bValue);
|
|
_rect.y += spacing;
|
|
task.bMax = EditorGUI.FloatField(_rect, "B Max", task.bMax);
|
|
_rect.y += spacing;
|
|
formulaDisplay += $"Rand({task.bValue}, {task.bMax}) ";
|
|
break;
|
|
}
|
|
}
|
|
|
|
EditorGUI.LabelField(_rect, task.targetName == "" ? "No Parameter" : formulaDisplay,
|
|
new GUIStyle(GUI.skin.label) { fontStyle = FontStyle.Bold });
|
|
|
|
EditorGUIUtility.labelWidth = originalLabelWidth;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Utility
|
|
|
|
private void AppendComponentMenu(GenericMenuBuilder genericMenuBuilder, ReorderableList list, bool isEnterList)
|
|
{
|
|
bool hasSelectedTask = list.index != -1;
|
|
bool hasTasks = list.count > 0;
|
|
|
|
if (isEnterList)
|
|
{
|
|
genericMenuBuilder.AddMenuItem("To Exit Task", hasTasks && hasSelectedTask, () => ConvertSelectedTask(list, _animatorDriver.EnterTasks, _animatorDriver.ExitTasks));
|
|
genericMenuBuilder.AddMenuItem("All to Exit Task", hasTasks, ConvertAllTasksToExit);
|
|
}
|
|
else
|
|
{
|
|
genericMenuBuilder.AddMenuItem("To Enter Task", hasTasks && hasSelectedTask, () => ConvertSelectedTask(list, _animatorDriver.ExitTasks, _animatorDriver.EnterTasks));
|
|
genericMenuBuilder.AddMenuItem("All to Enter Task", hasTasks, ConvertAllTasksToEnter);
|
|
}
|
|
}
|
|
|
|
private void ConvertAllTasksToEnter()
|
|
{
|
|
_animatorDriver.EnterTasks.AddRange(_animatorDriver.ExitTasks);
|
|
_animatorDriver.ExitTasks.Clear();
|
|
}
|
|
|
|
private void ConvertAllTasksToExit()
|
|
{
|
|
_animatorDriver.ExitTasks.AddRange(_animatorDriver.EnterTasks);
|
|
_animatorDriver.EnterTasks.Clear();
|
|
}
|
|
|
|
private void ConvertSelectedTask(ReorderableList list, List<AnimatorDriverTask> fromTasks, List<AnimatorDriverTask> toTasks)
|
|
{
|
|
if (list.index == -1 || list.index >= fromTasks.Count) return;
|
|
|
|
AnimatorDriverTask selectedTask = fromTasks[list.index];
|
|
fromTasks.RemoveAt(list.index);
|
|
toTasks.Add(selectedTask);
|
|
}
|
|
|
|
#endregion
|
|
} |