/************************************************************************************************************************/ /// <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>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; } var state = CurrentAnimations.GetArrayElementAtIndex(index); var speed = CurrentSpeeds.arraySize > 0 ? CurrentSpeeds.GetArrayElementAtIndex(index) : null; DoElementGUI(area, index, state, speed); }
/// <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); } }
/************************************************************************************************************************/ private void AddCalculateThresholdsFunction(UnityEditor.GenericMenu menu, string label, Func <Object, float, float> calculateThreshold) { AddPropertyModifierFunction(menu, label, (property) => { var count = CurrentAnimations.arraySize; for (int i = 0; i < count; i++) { var state = CurrentAnimations.GetArrayElementAtIndex(i).objectReferenceValue; if (state == null) { continue; } var threshold = CurrentThresholds.GetArrayElementAtIndex(i); var value = calculateThreshold(state, threshold.floatValue); if (!float.IsNaN(value)) { threshold.floatValue = value; } } }); }
/************************************************************************************************************************/ #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(); }); }); } }