717 lines
25 KiB
C#
717 lines
25 KiB
C#
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
|
|
|
|
#if UNITY_EDITOR && UNITY_IMGUI
|
|
|
|
using System;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using static Animancer.Editor.AnimancerGraphDrawer;
|
|
using static Animancer.Editor.AnimancerGUI;
|
|
using static Animancer.Editor.AnimancerStateDrawerColors;
|
|
using Object = UnityEngine.Object;
|
|
|
|
namespace Animancer.Editor
|
|
{
|
|
/// <summary>[Editor-Only] Draws the Inspector GUI for an <see cref="AnimancerState"/>.</summary>
|
|
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerStateDrawer_1
|
|
[CustomGUI(typeof(AnimancerState))]
|
|
public class AnimancerStateDrawer<T> : AnimancerNodeDrawer<T>
|
|
where T : AnimancerState
|
|
{
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
protected override bool AutoNormalizeSiblingWeights
|
|
=> AutoNormalizeWeights
|
|
&& typeof(T) != typeof(ManualMixerState);
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
private FastObjectField _NameField;
|
|
private FastObjectField _MainObjectField;
|
|
|
|
/// <summary>Draws the state's main label with a bar to indicate its current time.</summary>
|
|
protected override void DoLabelGUI(Rect area)
|
|
{
|
|
area = area.Expand(StandardSpacing, 0);
|
|
|
|
var wholeArea = area;
|
|
|
|
var effectiveWeight = Value.EffectiveWeight;
|
|
|
|
var highlightArea = default(Rect);
|
|
var isRepaint = Event.current.type == EventType.Repaint;
|
|
if (isRepaint)
|
|
{
|
|
EditorGUI.DrawRect(wholeArea, HeaderBackgroundColor);
|
|
|
|
highlightArea = DoTimeHighlightBarGUI(wholeArea, effectiveWeight);
|
|
|
|
DoEventsGUI(wholeArea);
|
|
|
|
ObjectHighlightGUI.Draw(wholeArea, Value);
|
|
}
|
|
|
|
DoWeightLabel(ref area, Value.Weight, effectiveWeight);
|
|
|
|
AnimationBindings.DoBindingMatchGUI(ref area, Value);
|
|
|
|
HandleLabelClick(wholeArea);
|
|
|
|
area = EditorGUI.IndentedRect(area);
|
|
|
|
var name = Value.DebugName ?? Value.Key;
|
|
var mainObject = Value.MainObject;
|
|
|
|
if (mainObject == null)
|
|
{
|
|
var value = name ?? Value;
|
|
var drawPing = value != Value;
|
|
_NameField.Draw(area, value, drawPing);
|
|
}
|
|
else if (ReferenceEquals(name, mainObject) ||
|
|
(name is Object nameObject && nameObject == mainObject) ||
|
|
(name is ITransition && Current != null && !Current.IsMainObjectUsedMultipleTimes(mainObject)))
|
|
{
|
|
_MainObjectField.Draw(area, mainObject, false);
|
|
}
|
|
else
|
|
{
|
|
if (name != null)
|
|
{
|
|
var nameArea = StealFromLeft(ref area, EditorGUIUtility.labelWidth - IndentSize);
|
|
_NameField.Draw(nameArea, name, true);
|
|
}
|
|
|
|
_MainObjectField.Draw(area, mainObject, false);
|
|
}
|
|
|
|
if (isRepaint)
|
|
DoDetailLinesGUI(wholeArea, highlightArea, effectiveWeight);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Draws a progress bar to show the animation time.</summary>
|
|
public Rect DoTimeHighlightBarGUI(Rect area, float effectiveWeight)
|
|
=> DoTimeHighlightBarGUI(
|
|
area,
|
|
Value.IsPlaying,
|
|
effectiveWeight,
|
|
Value.Time,
|
|
Value.EffectiveSpeed,
|
|
Value.Length,
|
|
Value.IsLooping);
|
|
|
|
/// <summary>Draws a progress bar to show the animation time.</summary>
|
|
public static Rect DoTimeHighlightBarGUI(
|
|
Rect area,
|
|
bool isPlaying,
|
|
float effectiveWeight,
|
|
float time,
|
|
float speed,
|
|
float length,
|
|
bool isLooping)
|
|
{
|
|
if (ScaleTimeBarByWeight)
|
|
{
|
|
var height = area.height;
|
|
area.height *= Mathf.Clamp01(effectiveWeight);
|
|
area.y += height - area.height;
|
|
}
|
|
|
|
var color = isPlaying ? PlayingBarColor : PausedBarColor;
|
|
|
|
var wrappedTime = GetWrappedTime(time, length, isLooping);
|
|
|
|
if (length == 0)
|
|
{
|
|
if (time == 0)
|
|
return area;
|
|
}
|
|
else
|
|
{
|
|
if (speed >= 0 || time == 0)
|
|
{
|
|
area.width *= Mathf.Clamp01(wrappedTime / length);
|
|
}
|
|
else
|
|
{
|
|
var xMax = area.xMax;
|
|
area.x += area.width * Mathf.Clamp01(wrappedTime / length);
|
|
area.x = Mathf.Floor(area.x);
|
|
area.xMax = xMax;
|
|
}
|
|
}
|
|
|
|
EditorGUI.DrawRect(area, color);
|
|
|
|
return area;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Draws lines for the current weight, time, and fade destination.</summary>
|
|
public void DoDetailLinesGUI(
|
|
Rect totalArea,
|
|
Rect highlightArea,
|
|
float effectiveWeight)
|
|
{
|
|
var length = Value.Length;
|
|
|
|
var speed = Value.Speed;
|
|
var speedSign = speed >= 0 ? 1 : -1;
|
|
var currentX = speed >= 0 ? highlightArea.xMax : highlightArea.xMin - 1;
|
|
var forwardEdge = speed >= 0 ? totalArea.xMax : totalArea.xMin - 1;
|
|
|
|
var color = FadeLineColor;
|
|
color.a = color.a * effectiveWeight * 0.75f + 0.25f;
|
|
|
|
if (Value.Time != 0 || Value.IsPlaying || Value.Weight != 0)
|
|
{
|
|
EditorGUI.DrawRect(
|
|
new(highlightArea.x, highlightArea.yMin, highlightArea.width, 1),
|
|
color);
|
|
|
|
if (length == 0)
|
|
return;
|
|
|
|
EditorGUI.DrawRect(
|
|
new(currentX - speedSign, totalArea.y, 1, totalArea.height),
|
|
color);
|
|
}
|
|
else if (length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!Value.IsPlaying)
|
|
return;
|
|
|
|
var fade = Value.FadeGroup;
|
|
if (fade == null || !fade.IsValid)
|
|
return;
|
|
|
|
var currentCorner = new Vector2(currentX, highlightArea.yMin);
|
|
|
|
var targetWeight = Value.TargetWeight;
|
|
var remainingFadeDuration = fade.RemainingFadeDuration;
|
|
|
|
var targetCorner = new Vector2(
|
|
currentCorner.x + speed * remainingFadeDuration / Value.Length * totalArea.width,
|
|
Mathf.Lerp(totalArea.yMax, totalArea.yMin, targetWeight));
|
|
|
|
var intersect = Mathf.InverseLerp(currentCorner.x, targetCorner.x, forwardEdge);
|
|
var end = Vector2.LerpUnclamped(currentCorner, targetCorner, intersect);
|
|
|
|
BeginTriangles(color);
|
|
|
|
DrawLineBatched(
|
|
currentCorner,
|
|
end,
|
|
1);
|
|
|
|
if (intersect < 1 && Value.IsLooping)
|
|
{
|
|
end.x -= speedSign * totalArea.width;
|
|
targetCorner.x -= speedSign * totalArea.width;
|
|
|
|
DrawLineBatched(
|
|
end,
|
|
targetCorner,
|
|
1);
|
|
}
|
|
|
|
EndTriangles();
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Draws marks on the timeline for each event.</summary>
|
|
private void DoEventsGUI(Rect area)
|
|
{
|
|
if (!ShowEvents)
|
|
return;
|
|
|
|
DoAnimancerEventsGUI(area);
|
|
DoAnimationEventsGUI(area);
|
|
}
|
|
|
|
/// <summary>Draws marks on the timeline for each Animancer Event.</summary>
|
|
private void DoAnimancerEventsGUI(Rect area)
|
|
{
|
|
var events = Value.SharedEvents;
|
|
if (events == null)
|
|
return;
|
|
|
|
for (int i = 0; i < events.Count; i++)
|
|
DoEventTick(area, events[i].normalizedTime);
|
|
|
|
if (events.OnEnd != null)
|
|
DoEventTick(area, events.GetRealNormalizedEndTime(Value.Speed));
|
|
}
|
|
|
|
/// <summary>Draws marks on the timeline for each Animation Event.</summary>
|
|
private void DoAnimationEventsGUI(Rect area)
|
|
{
|
|
var clip = Value.MainObject as AnimationClip;
|
|
if (clip == null)
|
|
return;
|
|
|
|
var inverseLength = 1f / Value.Length;
|
|
|
|
var events = clip.GetCachedEvents();
|
|
for (int i = 0; i < events.Length; i++)
|
|
DoEventTick(area, events[i].time * inverseLength);
|
|
}
|
|
|
|
/// <summary>Draws a mark on the timeline for an event.</summary>
|
|
private static void DoEventTick(Rect area, float normalizedTime)
|
|
{
|
|
if (normalizedTime >= 0 && normalizedTime <= 1)
|
|
{
|
|
var x = area.x + area.width * normalizedTime;
|
|
var eventArea = new Rect(x - 1, area.y, 2, area.height * 0.3f);
|
|
EditorGUI.DrawRect(eventArea, EventTickColor);
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Handles clicks on the label area.</summary>
|
|
private void HandleLabelClick(Rect area)
|
|
{
|
|
var currentEvent = Event.current;
|
|
if (currentEvent.type != EventType.MouseUp ||
|
|
currentEvent.button != 0 ||
|
|
!area.Contains(currentEvent.mousePosition))
|
|
return;
|
|
|
|
currentEvent.Use(0);
|
|
|
|
if (currentEvent.control)
|
|
FadeInTarget();
|
|
else
|
|
ToggleExpanded(currentEvent.alt);
|
|
}
|
|
|
|
/// <summary>Fades in the target state (or its parent state if not directly attached to a layer).</summary>
|
|
private void FadeInTarget()
|
|
{
|
|
Value.Graph.UnpauseGraph();
|
|
|
|
AnimancerState target = Value;
|
|
while (target != null)
|
|
{
|
|
var parent = target.Parent;
|
|
if (parent is AnimancerLayer layer)
|
|
{
|
|
var fadeDuration = target.CalculateEditorFadeDuration(
|
|
AnimancerGraph.DefaultFadeDuration);
|
|
layer.Play(target, fadeDuration);
|
|
return;
|
|
}
|
|
|
|
target = parent as AnimancerState;
|
|
}
|
|
}
|
|
|
|
/// <summary>Toggles the target's details between expanded and collapsed.</summary>
|
|
private void ToggleExpanded(bool toggleSiblings)
|
|
{
|
|
IsExpanded = !IsExpanded;
|
|
|
|
if (toggleSiblings)
|
|
{
|
|
var parent = Value.Parent;
|
|
var childCount = parent.ChildCount;
|
|
for (int i = 0; i < childCount; i++)
|
|
parent.GetChildNode(i)._IsInspectorExpanded = IsExpanded;
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
protected override void DoFoldoutGUI(Rect area)
|
|
{
|
|
var hierarchyMode = EditorGUIUtility.hierarchyMode;
|
|
EditorGUIUtility.hierarchyMode = true;
|
|
|
|
IsExpanded = EditorGUI.Foldout(area, IsExpanded, GUIContent.none, true);
|
|
|
|
EditorGUIUtility.hierarchyMode = hierarchyMode;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>
|
|
/// Gets the current <see cref="AnimancerState.Time"/>.
|
|
/// If the state is looping, the value is modulo by the <see cref="AnimancerState.Length"/>.
|
|
/// </summary>
|
|
private float GetWrappedTime(out float length)
|
|
=> GetWrappedTime(Value.Time, length = Value.Length, Value.IsLooping);
|
|
|
|
/// <summary>
|
|
/// Gets the current <see cref="AnimancerState.Time"/>.
|
|
/// If the state is looping, the value is modulo by the <see cref="AnimancerState.Length"/>.
|
|
/// </summary>
|
|
private static float GetWrappedTime(float time, float length, bool isLooping)
|
|
{
|
|
var wrappedTime = time;
|
|
|
|
if (isLooping)
|
|
{
|
|
wrappedTime = AnimancerUtilities.Wrap(wrappedTime, length);
|
|
if (wrappedTime == 0 && time != 0)
|
|
wrappedTime = length;
|
|
}
|
|
|
|
return wrappedTime;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
private FastObjectField _KeyField;
|
|
private FastObjectField _OwnerField;
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>The display name of the <see cref="AnimancerState.MainObject"/> field.</summary>
|
|
public virtual string MainObjectName
|
|
=> "Main Object";
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
protected override void DoDetailsGUI()
|
|
{
|
|
base.DoDetailsGUI();
|
|
|
|
if (!IsExpanded)
|
|
return;
|
|
|
|
EditorGUI.indentLevel++;
|
|
|
|
DoOptionalReferenceGUI(ref _KeyField, "Key", Value.Key);
|
|
DoOptionalReferenceGUI(ref _OwnerField, "Owner", Value.Owner);
|
|
|
|
var mainObject = Value.MainObject;
|
|
if (mainObject != null)
|
|
{
|
|
var mainObjectType = Value.MainObjectType ?? typeof(Object);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
var area = LayoutSingleLineRect(SpacingMode.Before);
|
|
|
|
mainObject = EditorGUI.ObjectField(
|
|
area,
|
|
MainObjectName,
|
|
mainObject,
|
|
mainObjectType,
|
|
true);
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
Value.MainObject = mainObject;
|
|
}
|
|
|
|
DoTimeSliderGUI();
|
|
DoNodeDetailsGUI();
|
|
DoOnEndGUI();
|
|
EditorGUI.indentLevel--;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Draws a `reference` if it isn't <c>null</c>.</summary>
|
|
private static void DoOptionalReferenceGUI(ref FastObjectField field, string label, object reference)
|
|
{
|
|
if (reference != null)
|
|
field.Draw(LayoutSingleLineRect(SpacingMode.Before), label, reference);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Draws a slider for controlling the current <see cref="AnimancerState.Time"/>.</summary>
|
|
private void DoTimeSliderGUI()
|
|
{
|
|
if (Value.Length <= 0)
|
|
return;
|
|
|
|
var time = GetWrappedTime(out var length);
|
|
|
|
if (length == 0)
|
|
return;
|
|
|
|
var area = LayoutSingleLineRect(SpacingMode.Before);
|
|
|
|
var normalized = DoNormalizedTimeToggle(ref area);
|
|
|
|
string label;
|
|
float max;
|
|
if (normalized)
|
|
{
|
|
label = "Normalized Time";
|
|
time /= length;
|
|
max = 1;
|
|
}
|
|
else
|
|
{
|
|
label = "Time";
|
|
max = length;
|
|
}
|
|
|
|
DoLoopCounterGUI(ref area, length);
|
|
|
|
EditorGUI.BeginChangeCheck();
|
|
|
|
label = BeginTightLabel(label);
|
|
time = EditorGUI.Slider(area, label, time, 0, max);
|
|
EndTightLabel();
|
|
|
|
if (TryUseClickEvent(area, 2))
|
|
time = 0;
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
if (normalized)
|
|
Value.NormalizedTime = time;
|
|
else
|
|
Value.Time = time;
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
private static bool DoNormalizedTimeToggle(ref Rect area)
|
|
{
|
|
using (var label = PooledGUIContent.Acquire("N"))
|
|
{
|
|
var style = MiniButtonStyle;
|
|
|
|
var width = style.CalculateWidth(label);
|
|
var toggleArea = StealFromRight(ref area, width);
|
|
|
|
UseNormalizedTimeSliders.Value = GUI.Toggle(toggleArea, UseNormalizedTimeSliders, label, style);
|
|
}
|
|
|
|
return UseNormalizedTimeSliders;
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
private static ConversionCache<int, string> _LoopCounterCache;
|
|
|
|
private void DoLoopCounterGUI(ref Rect area, float length)
|
|
{
|
|
_LoopCounterCache ??= new(x => $"x{x}");
|
|
|
|
string label;
|
|
var normalizedTime = Value.Time / length;
|
|
if (float.IsNaN(normalizedTime))
|
|
{
|
|
label = "NaN";
|
|
}
|
|
else
|
|
{
|
|
var loops = Mathf.FloorToInt(Value.Time / length);
|
|
label = _LoopCounterCache.Convert(loops);
|
|
}
|
|
|
|
var width = CalculateLabelWidth(label);
|
|
|
|
var labelArea = StealFromRight(ref area, width);
|
|
|
|
GUI.Label(labelArea, label);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
private void DoOnEndGUI()
|
|
{
|
|
var events = Value.SharedEvents;
|
|
if (events == null)
|
|
return;
|
|
|
|
var drawer = EventSequenceDrawer.Get(events);
|
|
var area = LayoutRect(drawer.CalculateHeight(events), SpacingMode.Before);
|
|
|
|
using (var label = PooledGUIContent.Acquire("Events"))
|
|
drawer.DoGUI(ref area, events, label);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
#region Context Menu
|
|
/************************************************************************************************************************/
|
|
|
|
/// <inheritdoc/>
|
|
protected override void PopulateContextMenu(GenericMenu menu)
|
|
{
|
|
AddContextMenuFunctions(menu);
|
|
|
|
menu.AddFunction("Play",
|
|
!Value.IsPlaying || Value.Weight != 1,
|
|
() =>
|
|
{
|
|
AnimancerState.SkipNextExpectFade();
|
|
Value.Graph.UnpauseGraph();
|
|
Value.Graph.Layers[0].Play(Value);
|
|
});
|
|
|
|
AnimancerEditorUtilities.AddFadeFunction(menu, "Cross Fade (Ctrl + Click)",
|
|
Value.Weight != 1,
|
|
Value,
|
|
duration =>
|
|
{
|
|
AnimancerState.SkipNextExpectFade();
|
|
Value.Graph.UnpauseGraph();
|
|
Value.Graph.Layers[0].Play(Value, duration);
|
|
});
|
|
|
|
menu.AddSeparator("");
|
|
menu.AddItem(new("Destroy State"),
|
|
false,
|
|
() => Value.Destroy());
|
|
|
|
menu.AddSeparator("");
|
|
|
|
AddDisplayOptions(menu);
|
|
|
|
AnimancerEditorUtilities.AddDocumentationLink(
|
|
menu,
|
|
"State Documentation",
|
|
Strings.DocsURLs.States);
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Adds the details of this state to the `menu`.</summary>
|
|
protected virtual void AddContextMenuFunctions(GenericMenu menu)
|
|
{
|
|
menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.Key)}: {AnimancerUtilities.ToStringOrNull(Value.Key)}"));
|
|
menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.Owner)}: {AnimancerUtilities.ToStringOrNull(Value.Owner)}"));
|
|
|
|
var length = Value.Length;
|
|
if (!float.IsNaN(length))
|
|
menu.AddDisabledItem(new($"{DetailsPrefix}{nameof(Value.Length)}: {length}"));
|
|
|
|
menu.AddDisabledItem(new($"{DetailsPrefix}Playable Path: {Value.GetPath()}"));
|
|
|
|
var mainAsset = Value.MainObject;
|
|
if (mainAsset != null)
|
|
{
|
|
var assetPath = AssetDatabase.GetAssetPath(mainAsset);
|
|
if (assetPath != null)
|
|
menu.AddDisabledItem(new($"{DetailsPrefix}Asset Path: {assetPath.Replace("/", "->")}"));
|
|
}
|
|
|
|
var events = Value.SharedEvents;
|
|
if (events != null)
|
|
{
|
|
for (int i = 0; i < events.Count; i++)
|
|
{
|
|
var index = i;
|
|
var name = events.GetName(i);
|
|
AddEventFunctions(
|
|
menu,
|
|
name.IsNullOrEmpty() ? "Event " + index : name,
|
|
name,
|
|
events[index],
|
|
() => events.SetCallback(index, AnimancerEvent.InvokeBoundCallback),
|
|
() => events.Remove(index));
|
|
}
|
|
|
|
AddEventFunctions(
|
|
menu,
|
|
"End Event",
|
|
default,
|
|
events.EndEvent,
|
|
() => events.EndEvent = new(float.NaN, null), null);
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
|
|
private void AddEventFunctions(
|
|
GenericMenu menu,
|
|
string displayName,
|
|
StringReference name,
|
|
AnimancerEvent animancerEvent,
|
|
GenericMenu.MenuFunction clearEvent,
|
|
GenericMenu.MenuFunction removeEvent)
|
|
{
|
|
displayName = $"Events/{displayName}/";
|
|
|
|
menu.AddDisabledItem(new($"{displayName}{nameof(AnimancerState.NormalizedTime)}: {animancerEvent.normalizedTime}"));
|
|
|
|
bool canInvoke;
|
|
if (animancerEvent.callback == null)
|
|
{
|
|
menu.AddDisabledItem(new(displayName + "Callback: null"));
|
|
canInvoke = false;
|
|
}
|
|
else if (animancerEvent.callback == AnimancerEvent.DummyCallback)
|
|
{
|
|
menu.AddDisabledItem(new(displayName + "Callback: Dummy"));
|
|
canInvoke = false;
|
|
}
|
|
else
|
|
{
|
|
var label = displayName +
|
|
(animancerEvent.callback.Target != null
|
|
? ("Target: " + animancerEvent.callback.Target)
|
|
: "Target: null");
|
|
|
|
var targetObject = animancerEvent.callback.Target as Object;
|
|
menu.AddFunction(label,
|
|
targetObject != null,
|
|
() => Selection.activeObject = targetObject);
|
|
|
|
menu.AddDisabledItem(new(
|
|
$"{displayName}Declaring Type: {animancerEvent.callback.Method.DeclaringType.GetNameCS()}"));
|
|
|
|
menu.AddDisabledItem(new(
|
|
$"{displayName}Method: {animancerEvent.callback.Method}"));
|
|
|
|
canInvoke = true;
|
|
}
|
|
|
|
if (clearEvent != null)
|
|
menu.AddFunction(displayName + "Clear", canInvoke || !float.IsNaN(animancerEvent.normalizedTime), clearEvent);
|
|
|
|
if (removeEvent != null)
|
|
menu.AddFunction(displayName + "Remove", true, removeEvent);
|
|
|
|
menu.AddFunction(displayName + "Invoke", canInvoke, () => animancerEvent.DelayInvoke(name, Value));
|
|
}
|
|
|
|
/************************************************************************************************************************/
|
|
#endregion
|
|
/************************************************************************************************************************/
|
|
}
|
|
|
|
/// <summary>[Editor-Only] Colors used by <see cref="AnimancerStateDrawer{T}"/>.</summary>
|
|
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerStateDrawerColors
|
|
public static class AnimancerStateDrawerColors
|
|
{
|
|
/************************************************************************************************************************/
|
|
|
|
/// <summary>Colors used by this system.</summary>
|
|
public static readonly Color
|
|
HeaderBackgroundColor = Grey(0.35f, 0.35f),
|
|
PlayingBarColor = new(0.15f, 0.7f, 0.15f, 0.4f),// Green = Playing.
|
|
PausedBarColor = new(0.7f, 0.7f, 0.15f, 0.4f),// Yelow = Paused.
|
|
FadeLineColor = new(0.3f, 1, 0.3f, 1);
|
|
|
|
/// <summary>Colors used by this system.</summary>
|
|
public static Color EventTickColor
|
|
=> Grey(EditorGUIUtility.isProSkin ? 0.8f : 0.2f, 0.8f);
|
|
|
|
/************************************************************************************************************************/
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|