123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using Object = UnityEngine.Object;
- #if UNITY_EDITOR
- using Animancer.Editor;
- using UnityEditor;
- using UnityEditorInternal;
- #endif
- namespace Animancer
- {
- /// <inheritdoc/>
- /// https://kybernetik.com.au/animancer/api/Animancer/ManualMixerTransition
- [Serializable]
- public class ManualMixerTransition : ManualMixerTransition<ManualMixerState>,
- ManualMixerState.ITransition, ICopyable<ManualMixerTransition>
- {
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public override ManualMixerState CreateState()
- {
- State = new ManualMixerState();
- InitializeState();
- return State;
- }
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public virtual void CopyFrom(ManualMixerTransition copyFrom)
- {
- CopyFrom((ManualMixerTransition<ManualMixerState>)copyFrom);
- }
- /************************************************************************************************************************/
- #if UNITY_EDITOR
- /************************************************************************************************************************/
- /// <inheritdoc/>
- [CustomPropertyDrawer(typeof(ManualMixerTransition), true)]
- public class Drawer : TransitionDrawer
- {
- /************************************************************************************************************************/
- /// <summary>The property this drawer is currently drawing.</summary>
- /// <remarks>Normally each property has its own drawer, but arrays share a single drawer for all elements.</remarks>
- public static SerializedProperty CurrentProperty { get; private set; }
- /// <summary>The <see cref="ManualMixerTransition{TState}.Animations"/> field.</summary>
- public static SerializedProperty CurrentAnimations { get; private set; }
- /// <summary>The <see cref="ManualMixerTransition{TState}.Speeds"/> field.</summary>
- public static SerializedProperty CurrentSpeeds { get; private set; }
- /// <summary>The <see cref="ManualMixerTransition{TState}.SynchronizeChildren"/> field.</summary>
- public static SerializedProperty CurrentSynchronizeChildren { get; private set; }
- private readonly Dictionary<string, ReorderableList>
- PropertyPathToStates = new Dictionary<string, ReorderableList>();
- private ReorderableList _MultiSelectDummyList;
- /************************************************************************************************************************/
- /// <summary>Gather the details of the `property`.</summary>
- /// <remarks>
- /// This method gets called by every <see cref="GetPropertyHeight"/> and <see cref="OnGUI"/> call since
- /// Unity uses the same <see cref="PropertyDrawer"/> instance for each element in a collection, so it
- /// needs to gather the details associated with the current property.
- /// </remarks>
- protected virtual ReorderableList GatherDetails(SerializedProperty property)
- {
- InitializeMode(property);
- GatherSubProperties(property);
- if (property.hasMultipleDifferentValues)
- {
- if (_MultiSelectDummyList == null)
- {
- _MultiSelectDummyList = new ReorderableList(new List<Object>(), typeof(Object))
- {
- elementHeight = AnimancerGUI.LineHeight,
- displayAdd = false,
- displayRemove = false,
- footerHeight = 0,
- drawHeaderCallback = DoAnimationHeaderGUI,
- drawNoneElementCallback = area => EditorGUI.LabelField(area,
- "Multi-editing animations is not supported"),
- };
- }
- return _MultiSelectDummyList;
- }
- if (CurrentAnimations == null)
- return null;
- var path = property.propertyPath;
- if (!PropertyPathToStates.TryGetValue(path, out var states))
- {
- states = new ReorderableList(CurrentAnimations.serializedObject, CurrentAnimations)
- {
- drawHeaderCallback = DoChildListHeaderGUI,
- elementHeightCallback = GetElementHeight,
- drawElementCallback = DoElementGUI,
- onAddCallback = OnAddElement,
- onRemoveCallback = OnRemoveElement,
- onReorderCallbackWithDetails = OnReorderList,
- drawFooterCallback = DoChildListFooterGUI,
- };
- PropertyPathToStates.Add(path, states);
- }
- states.serializedProperty = CurrentAnimations;
- return states;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Called every time a `property` is drawn to find the relevant child properties and store them to be
- /// used in <see cref="GetPropertyHeight"/> and <see cref="OnGUI"/>.
- /// </summary>
- protected virtual void GatherSubProperties(SerializedProperty property)
- {
- CurrentProperty = property;
- CurrentAnimations = property.FindPropertyRelative(AnimationsField);
- CurrentSpeeds = property.FindPropertyRelative(SpeedsField);
- CurrentSynchronizeChildren = property.FindPropertyRelative(SynchronizeChildrenField);
- if (!property.hasMultipleDifferentValues &&
- CurrentAnimations != null &&
- CurrentSpeeds != null &&
- CurrentSpeeds.arraySize != 0)
- CurrentSpeeds.arraySize = CurrentAnimations.arraySize;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Adds a menu item that will call <see cref="GatherSubProperties"/> then run the specified
- /// `function`.
- /// </summary>
- protected void AddPropertyModifierFunction(GenericMenu menu, string label,
- MenuFunctionState state, Action<SerializedProperty> function)
- {
- Serialization.AddPropertyModifierFunction(menu, CurrentProperty, label, state, (property) =>
- {
- GatherSubProperties(property);
- function(property);
- });
- }
- /// <summary>
- /// Adds a menu item that will call <see cref="GatherSubProperties"/> then run the specified
- /// `function`.
- /// </summary>
- protected void AddPropertyModifierFunction(GenericMenu menu, string label,
- Action<SerializedProperty> function)
- {
- Serialization.AddPropertyModifierFunction(menu, CurrentProperty, label, (property) =>
- {
- GatherSubProperties(property);
- function(property);
- });
- }
- /************************************************************************************************************************/
- /// <inheritdoc/>
- public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
- {
- var height = EditorGUI.GetPropertyHeight(property, label);
- if (property.isExpanded)
- {
- var states = GatherDetails(property);
- if (states != null)
- height += AnimancerGUI.StandardSpacing + states.GetHeight();
- if (CurrentAnimations != null)
- height -= AnimancerGUI.StandardSpacing + EditorGUI.GetPropertyHeight(CurrentAnimations, label);
- if (CurrentSpeeds != null)
- height -= AnimancerGUI.StandardSpacing + EditorGUI.GetPropertyHeight(CurrentSpeeds, label);
- if (CurrentSynchronizeChildren != null)
- height -= AnimancerGUI.StandardSpacing + EditorGUI.GetPropertyHeight(CurrentSynchronizeChildren, label);
- }
- return height;
- }
- /************************************************************************************************************************/
- private SerializedProperty _RootProperty;
- private ReorderableList _CurrentChildList;
- /// <inheritdoc/>
- public override void OnGUI(Rect area, SerializedProperty property, GUIContent label)
- {
- _RootProperty = null;
- base.OnGUI(area, property, label);
- if (_RootProperty == null ||
- !_RootProperty.isExpanded)
- return;
- using (DrawerContext.Get(_RootProperty))
- {
- if (Context.Transition == null)
- return;
- _CurrentChildList = GatherDetails(_RootProperty);
- if (_CurrentChildList == null)
- return;
- var indentLevel = EditorGUI.indentLevel;
- area.yMin = area.yMax - _CurrentChildList.GetHeight();
- EditorGUI.indentLevel++;
- area = EditorGUI.IndentedRect(area);
- EditorGUI.indentLevel = 0;
- _CurrentChildList.DoList(area);
- EditorGUI.indentLevel = indentLevel;
- TryCollapseArrays();
- }
- }
- /************************************************************************************************************************/
- /// <inheritdoc/>
- protected override void DoChildPropertyGUI(ref Rect area,
- SerializedProperty rootProperty, SerializedProperty property, GUIContent label)
- {
- if (Context?.Transition != null)
- {
- area.height = 0;
- // If we find the Animations property, hide it to draw it last.
- var path = property.propertyPath;
- if (path.EndsWith("." + AnimationsField))
- {
- _RootProperty = rootProperty;
- return;
- }
- else if (_RootProperty != null)
- {
- // If we already found the Animations property, also hide Speeds and Synchronize Children.
- if (path.EndsWith("." + SpeedsField) ||
- path.EndsWith("." + SynchronizeChildrenField))
- return;
- }
- }
- base.DoChildPropertyGUI(ref area, rootProperty, property, label);
- }
- /************************************************************************************************************************/
- private static float _SpeedLabelWidth;
- private static float _SyncLabelWidth;
- /// <summary>Splits the specified `area` into separate sections.</summary>
- protected static void SplitListRect(Rect area, bool isHeader, out Rect animation, out Rect speed, out Rect sync)
- {
- if (_SpeedLabelWidth == 0)
- _SpeedLabelWidth = AnimancerGUI.CalculateWidth(EditorStyles.popup, "Speed");
- if (_SyncLabelWidth == 0)
- _SyncLabelWidth = AnimancerGUI.CalculateWidth(EditorStyles.popup, "Sync");
- var spacing = AnimancerGUI.StandardSpacing;
- var syncWidth = isHeader ?
- _SyncLabelWidth :
- AnimancerGUI.ToggleWidth - spacing;
- var speedWidth = _SpeedLabelWidth + _SyncLabelWidth - syncWidth;
- if (!isHeader)
- {
- // Don't use Clamp because the max might be smaller than the min.
- var max = Math.Max(area.height, area.width * 0.25f - 30);
- speedWidth = Math.Min(speedWidth, max);
- }
- area.width += spacing;
- sync = AnimancerGUI.StealFromRight(ref area, syncWidth, spacing);
- speed = AnimancerGUI.StealFromRight(ref area, speedWidth, spacing);
- animation = area;
- }
- /************************************************************************************************************************/
- #region Headers
- /************************************************************************************************************************/
- /// <summary>Draws the headdings of the child list.</summary>
- protected virtual void DoChildListHeaderGUI(Rect area)
- {
- SplitListRect(area, true, out var animationArea, out var speedArea, out var syncArea);
- DoAnimationHeaderGUI(animationArea);
- DoSpeedHeaderGUI(speedArea);
- DoSyncHeaderGUI(syncArea);
- }
- /************************************************************************************************************************/
- /// <summary>Draws an "Animation" header.</summary>
- protected static void DoAnimationHeaderGUI(Rect area)
- {
- using (ObjectPool.Disposable.AcquireContent(out var label, "Animation",
- $"The animations that will be used for each child state" +
- $"\n\nCtrl + Click to allow picking Transition Assets (or anything that implements {nameof(ITransition)})"))
- {
- DoHeaderDropdownGUI(area, CurrentAnimations, label, null);
- }
- }
- /************************************************************************************************************************/
- #region Speeds
- /************************************************************************************************************************/
- /// <summary>Draws a "Speed" header.</summary>
- protected void DoSpeedHeaderGUI(Rect area)
- {
- using (ObjectPool.Disposable.AcquireContent(out var label, "Speed", Strings.Tooltips.Speed))
- {
- DoHeaderDropdownGUI(area, CurrentSpeeds, label, (menu) =>
- {
- AddPropertyModifierFunction(menu, "Reset All to 1",
- CurrentSpeeds.arraySize == 0 ? MenuFunctionState.Selected : MenuFunctionState.Normal,
- (_) => CurrentSpeeds.arraySize = 0);
- AddPropertyModifierFunction(menu, "Normalize Durations", MenuFunctionState.Normal, NormalizeDurations);
- });
- }
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Recalculates the <see cref="CurrentSpeeds"/> depending on the <see cref="AnimationClip.length"/> of
- /// their animations so that they all take the same amount of time to play fully.
- /// </summary>
- private static void NormalizeDurations(SerializedProperty property)
- {
- var speedCount = CurrentSpeeds.arraySize;
- var lengths = new float[CurrentAnimations.arraySize];
- if (lengths.Length <= 1)
- return;
- int nonZeroLengths = 0;
- float totalLength = 0;
- float totalSpeed = 0;
- for (int i = 0; i < lengths.Length; i++)
- {
- var state = CurrentAnimations.GetArrayElementAtIndex(i).objectReferenceValue;
- if (AnimancerUtilities.TryGetLength(state, out var length) &&
- length > 0)
- {
- nonZeroLengths++;
- totalLength += length;
- lengths[i] = length;
- if (speedCount > 0)
- totalSpeed += CurrentSpeeds.GetArrayElementAtIndex(i).floatValue;
- }
- }
- if (nonZeroLengths == 0)
- return;
- var averageLength = totalLength / nonZeroLengths;
- var averageSpeed = speedCount > 0 ? totalSpeed / nonZeroLengths : 1;
- CurrentSpeeds.arraySize = lengths.Length;
- InitializeSpeeds(speedCount);
- for (int i = 0; i < lengths.Length; i++)
- {
- if (lengths[i] == 0)
- continue;
- CurrentSpeeds.GetArrayElementAtIndex(i).floatValue = averageSpeed * lengths[i] / averageLength;
- }
- TryCollapseArrays();
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Initializes every element in the <see cref="CurrentSpeeds"/> array from the `start` to the end of
- /// the array to contain a value of 1.
- /// </summary>
- public static void InitializeSpeeds(int start)
- {
- var count = CurrentSpeeds.arraySize;
- while (start < count)
- CurrentSpeeds.GetArrayElementAtIndex(start++).floatValue = 1;
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- #region Sync
- /************************************************************************************************************************/
- /// <summary>Draws a "Sync" header.</summary>
- protected void DoSyncHeaderGUI(Rect area)
- {
- using (ObjectPool.Disposable.AcquireContent(out var label, "Sync",
- "Determines which child states have their normalized times constantly synchronized"))
- {
- DoHeaderDropdownGUI(area, CurrentSpeeds, label, (menu) =>
- {
- var syncCount = CurrentSynchronizeChildren.arraySize;
- var allState = syncCount == 0 ? MenuFunctionState.Selected : MenuFunctionState.Normal;
- AddPropertyModifierFunction(menu, "All", allState,
- (_) => CurrentSynchronizeChildren.arraySize = 0);
- var syncNone = syncCount == CurrentAnimations.arraySize;
- if (syncNone)
- {
- for (int i = 0; i < syncCount; i++)
- {
- if (CurrentSynchronizeChildren.GetArrayElementAtIndex(i).boolValue)
- {
- syncNone = false;
- break;
- }
- }
- }
- var noneState = syncNone ? MenuFunctionState.Selected : MenuFunctionState.Normal;
- AddPropertyModifierFunction(menu, "None", noneState, (_) =>
- {
- var count = CurrentSynchronizeChildren.arraySize = CurrentAnimations.arraySize;
- for (int i = 0; i < count; i++)
- CurrentSynchronizeChildren.GetArrayElementAtIndex(i).boolValue = false;
- });
- AddPropertyModifierFunction(menu, "Invert", MenuFunctionState.Normal, (_) =>
- {
- var count = CurrentSynchronizeChildren.arraySize;
- for (int i = 0; i < count; i++)
- {
- var property = CurrentSynchronizeChildren.GetArrayElementAtIndex(i);
- property.boolValue = !property.boolValue;
- }
- var newCount = CurrentSynchronizeChildren.arraySize = CurrentAnimations.arraySize;
- for (int i = count; i < newCount; i++)
- CurrentSynchronizeChildren.GetArrayElementAtIndex(i).boolValue = false;
- });
- AddPropertyModifierFunction(menu, "Non-Stationary", MenuFunctionState.Normal, (_) =>
- {
- var count = CurrentAnimations.arraySize;
- for (int i = 0; i < count; i++)
- {
- var state = CurrentAnimations.GetArrayElementAtIndex(i).objectReferenceValue;
- if (state == null)
- continue;
- if (i >= syncCount)
- {
- CurrentSynchronizeChildren.arraySize = i + 1;
- for (int j = syncCount; j < i; j++)
- CurrentSynchronizeChildren.GetArrayElementAtIndex(j).boolValue = true;
- syncCount = i + 1;
- }
- CurrentSynchronizeChildren.GetArrayElementAtIndex(i).boolValue =
- AnimancerUtilities.TryGetAverageVelocity(state, out var velocity) &&
- velocity != default;
- }
- TryCollapseSync();
- });
- });
- }
- }
- /************************************************************************************************************************/
- private static void SyncNone()
- {
- var count = CurrentSynchronizeChildren.arraySize = CurrentAnimations.arraySize;
- for (int i = 0; i < count; i++)
- CurrentSynchronizeChildren.GetArrayElementAtIndex(i).boolValue = false;
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- /// <summary>Draws the GUI for a header dropdown button.</summary>
- public static void DoHeaderDropdownGUI(Rect area, SerializedProperty property, GUIContent content,
- Action<GenericMenu> populateMenu)
- {
- if (property != null)
- EditorGUI.BeginProperty(area, GUIContent.none, property);
- if (populateMenu != null)
- {
- if (EditorGUI.DropdownButton(area, content, FocusType.Passive))
- {
- var menu = new GenericMenu();
- populateMenu(menu);
- menu.ShowAsContext();
- }
- }
- else
- {
- GUI.Label(area, content);
- }
- if (property != null)
- EditorGUI.EndProperty();
- }
- /************************************************************************************************************************/
- /// <summary>Draws the footer of the child list.</summary>
- protected virtual void DoChildListFooterGUI(Rect area)
- {
- ReorderableList.defaultBehaviours.DrawFooter(area, _CurrentChildList);
- EditorGUI.BeginChangeCheck();
- area.xMax = EditorGUIUtility.labelWidth + AnimancerGUI.IndentSize;
- area.y++;
- area.height = AnimancerGUI.LineHeight;
- using (ObjectPool.Disposable.AcquireContent(out var label, "Count"))
- {
- var indentLevel = EditorGUI.indentLevel;
- EditorGUI.indentLevel = 0;
- var labelWidth = EditorGUIUtility.labelWidth;
- EditorGUIUtility.labelWidth = AnimancerGUI.CalculateLabelWidth(label.text);
- var count = EditorGUI.DelayedIntField(area, label, _CurrentChildList.count);
- if (EditorGUI.EndChangeCheck())
- ResizeList(count);
- EditorGUIUtility.labelWidth = labelWidth;
- EditorGUI.indentLevel = indentLevel;
- }
- }
- /************************************************************************************************************************/
- #endregion
- /************************************************************************************************************************/
- /// <summary>Calculates the height of the state at the specified `index`.</summary>
- protected virtual float GetElementHeight(int index) => AnimancerGUI.LineHeight;
- /************************************************************************************************************************/
- /// <summary>Draws the GUI of the state at the specified `index`.</summary>
- private void DoElementGUI(Rect area, int index, bool isActive, bool isFocused)
- {
- if (index < 0 || index > CurrentAnimations.arraySize)
- return;
- area.height = AnimancerGUI.LineHeight;
- var state = CurrentAnimations.GetArrayElementAtIndex(index);
- var speed = CurrentSpeeds.arraySize > 0 ? CurrentSpeeds.GetArrayElementAtIndex(index) : null;
- DoElementGUI(area, index, state, speed);
- }
- /************************************************************************************************************************/
- /// <summary>Draws the GUI of the state at the specified `index`.</summary>
- protected virtual void DoElementGUI(Rect area, int index,
- SerializedProperty state, SerializedProperty speed)
- {
- SplitListRect(area, false, out var animationArea, out var speedArea, out var syncArea);
- DoElementGUI(animationArea, speedArea, syncArea, index, state, speed);
- }
- /// <summary>Draws the GUI of the state at the specified `index`.</summary>
- protected void DoElementGUI(Rect animationArea, Rect speedArea, Rect syncArea, int index,
- SerializedProperty state, SerializedProperty speed)
- {
- DoAnimationField(animationArea, state);
- if (speed != null)
- {
- EditorGUI.PropertyField(speedArea, speed, GUIContent.none);
- }
- else// If this element doesn't have its own speed property, just show 1.
- {
- EditorGUI.BeginProperty(speedArea, GUIContent.none, CurrentSpeeds);
- var value = Units.UnitsAttribute.DoSpecialFloatField(
- speedArea, null, 1, Units.AnimationSpeedAttribute.DisplayConverters[0]);
- // Middle Click toggles from 1 to -1.
- if (AnimancerGUI.TryUseClickEvent(speedArea, 2))
- value = -1;
- if (value != 1)
- {
- CurrentSpeeds.InsertArrayElementAtIndex(0);
- CurrentSpeeds.GetArrayElementAtIndex(0).floatValue = 1;
- CurrentSpeeds.arraySize = CurrentAnimations.arraySize;
- CurrentSpeeds.GetArrayElementAtIndex(index).floatValue = value;
- }
- EditorGUI.EndProperty();
- }
- DoSyncToggleGUI(syncArea, index);
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Draws an <see cref="EditorGUI.ObjectField(Rect, GUIContent, Object, Type, bool)"/> that accepts
- /// <see cref="AnimationClip"/>s and <see cref="ITransition"/>s
- /// </summary>
- public static void DoAnimationField(Rect area, SerializedProperty property)
- {
- EditorGUI.BeginProperty(area, GUIContent.none, property);
- var targetObject = property.serializedObject.targetObject;
- var oldReference = property.objectReferenceValue;
- var currentEvent = Event.current;
- var isDrag =
- currentEvent.type == EventType.DragUpdated ||
- currentEvent.type == EventType.DragPerform;
- var type =
- isDrag ||
- currentEvent.control ||
- currentEvent.commandName == "ObjectSelectorUpdated" ?
- typeof(Object) : typeof(AnimationClip);
- var allowSceneObjects = targetObject != null && !EditorUtility.IsPersistent(targetObject);
- EditorGUI.BeginChangeCheck();
- var newReference = EditorGUI.ObjectField(area, GUIContent.none, oldReference, type, allowSceneObjects);
- if (EditorGUI.EndChangeCheck())
- {
- if (newReference == null || (IsClipOrTransition(newReference) && newReference != targetObject))
- property.objectReferenceValue = newReference;
- }
- if (isDrag && area.Contains(currentEvent.mousePosition))
- {
- var objects = DragAndDrop.objectReferences;
- if (objects.Length != 1 ||
- !IsClipOrTransition(objects[0]) ||
- objects[0] == targetObject)
- DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
- }
- EditorGUI.EndProperty();
- }
- /// <summary>Is the `clipOrTransition` an <see cref="AnimationClip"/> or <see cref="ITransition"/>?</summary>
- public static bool IsClipOrTransition(object clipOrTransition)
- => clipOrTransition is AnimationClip || clipOrTransition is ITransition;
- /************************************************************************************************************************/
- /// <summary>
- /// Draws a toggle to enable or disable <see cref="MixerState.SynchronizedChildren"/> for the child at
- /// the specified `index`.
- /// </summary>
- protected void DoSyncToggleGUI(Rect area, int index)
- {
- var syncProperty = CurrentSynchronizeChildren;
- var syncFlagCount = syncProperty.arraySize;
- var enabled = true;
- if (index < syncFlagCount)
- {
- syncProperty = syncProperty.GetArrayElementAtIndex(index);
- enabled = syncProperty.boolValue;
- }
- EditorGUI.BeginChangeCheck();
- EditorGUI.BeginProperty(area, GUIContent.none, syncProperty);
- enabled = GUI.Toggle(area, enabled, GUIContent.none);
- EditorGUI.EndProperty();
- if (EditorGUI.EndChangeCheck())
- {
- if (index < syncFlagCount)
- {
- syncProperty.boolValue = enabled;
- }
- else
- {
- syncProperty.arraySize = index + 1;
- for (int i = syncFlagCount; i < index; i++)
- {
- syncProperty.GetArrayElementAtIndex(i).boolValue = true;
- }
- syncProperty.GetArrayElementAtIndex(index).boolValue = enabled;
- }
- }
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Called when adding a new state to the list to ensure that any other relevant arrays have new
- /// elements added as well.
- /// </summary>
- private void OnAddElement(ReorderableList list)
- {
- var index = list.index;
- if (index < 0 || Event.current.button == 1)// Right Click to add at the end.
- {
- index = CurrentAnimations.arraySize - 1;
- if (index < 0)
- index = 0;
- }
- OnAddElement(index);
- }
- /// <summary>
- /// Called when adding a new state to the list to ensure that any other relevant arrays have new
- /// elements added as well.
- /// </summary>
- protected virtual void OnAddElement(int index)
- {
- CurrentAnimations.InsertArrayElementAtIndex(index);
- if (CurrentSpeeds.arraySize > 0)
- CurrentSpeeds.InsertArrayElementAtIndex(index);
- if (CurrentSynchronizeChildren.arraySize > index)
- CurrentSynchronizeChildren.InsertArrayElementAtIndex(index);
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Called when removing a state from the list to ensure that any other relevant arrays have elements
- /// removed as well.
- /// </summary>
- protected virtual void OnRemoveElement(ReorderableList list)
- {
- var index = list.index;
- Serialization.RemoveArrayElement(CurrentAnimations, index);
- if (CurrentSpeeds.arraySize > index)
- Serialization.RemoveArrayElement(CurrentSpeeds, index);
- if (CurrentSynchronizeChildren.arraySize > index)
- Serialization.RemoveArrayElement(CurrentSynchronizeChildren, index);
- }
- /************************************************************************************************************************/
- /// <summary>Sets the number of items in the child list.</summary>
- protected virtual void ResizeList(int size)
- {
- CurrentAnimations.arraySize = size;
- if (CurrentSpeeds.arraySize > size)
- CurrentSpeeds.arraySize = size;
- if (CurrentSynchronizeChildren.arraySize > size)
- CurrentSynchronizeChildren.arraySize = size;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Called when reordering states in the list to ensure that any other relevant arrays have their
- /// corresponding elements reordered as well.
- /// </summary>
- protected virtual void OnReorderList(ReorderableList list, int oldIndex, int newIndex)
- {
- CurrentSpeeds.MoveArrayElement(oldIndex, newIndex);
- var syncCount = CurrentSynchronizeChildren.arraySize;
- if (Math.Max(oldIndex, newIndex) >= syncCount)
- {
- CurrentSynchronizeChildren.arraySize++;
- CurrentSynchronizeChildren.GetArrayElementAtIndex(syncCount).boolValue = true;
- CurrentSynchronizeChildren.arraySize = newIndex + 1;
- }
- CurrentSynchronizeChildren.MoveArrayElement(oldIndex, newIndex);
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Calls <see cref="TryCollapseSpeeds"/> and <see cref="TryCollapseSync"/>.
- /// </summary>
- public static void TryCollapseArrays()
- {
- if (CurrentProperty == null ||
- CurrentProperty.hasMultipleDifferentValues)
- return;
- TryCollapseSpeeds();
- TryCollapseSync();
- }
- /************************************************************************************************************************/
- /// <summary>
- /// If every element in the <see cref="CurrentSpeeds"/> array is 1, this method sets the array size to 0.
- /// </summary>
- public static void TryCollapseSpeeds()
- {
- var property = CurrentSpeeds;
- if (property == null)
- return;
- var speedCount = property.arraySize;
- if (speedCount <= 0)
- return;
- for (int i = 0; i < speedCount; i++)
- {
- if (property.GetArrayElementAtIndex(i).floatValue != 1)
- return;
- }
- property.arraySize = 0;
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Removes any true elements from the end of the <see cref="CurrentSynchronizeChildren"/> array.
- /// </summary>
- public static void TryCollapseSync()
- {
- var property = CurrentSynchronizeChildren;
- if (property == null)
- return;
- var count = property.arraySize;
- var changed = false;
- for (int i = count - 1; i >= 0; i--)
- {
- if (property.GetArrayElementAtIndex(i).boolValue)
- {
- count = i;
- changed = true;
- }
- else
- {
- break;
- }
- }
- if (changed)
- property.arraySize = count;
- }
- /************************************************************************************************************************/
- }
- /************************************************************************************************************************/
- #endif
- /************************************************************************************************************************/
- }
- }
|