chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

@@ -0,0 +1,313 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik //
#if UNITY_EDITOR
using Animancer.TransitionLibraries;
using System;
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor.TransitionLibraries
{
/// <summary>[Editor-Only]
/// A dummy object for tracking the selection within the <see cref="TransitionLibraryWindow"/>
/// and showing its details in the Inspector.
/// </summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibrarySelection
[AnimancerHelpUrl(typeof(TransitionLibrarySelection))]
public class TransitionLibrarySelection : ScriptableObject
{
/************************************************************************************************************************/
/// <summary>[Editor-Only] Types of objects can be selected.</summary>
public enum SelectionType
{
/// <summary>Nothing selected.</summary>
None,
/// <summary>The main library.</summary>
Library,
/// <summary>A from-transition.</summary>
FromTransition,
/// <summary>A to-transition.</summary>
ToTransition,
/// <summary>A modifier for a particular from-to transition combination.</summary>
Modifier,
/// <summary>A <see cref="TransitionGroup"/>.</summary>
Group,
}
/************************************************************************************************************************/
[SerializeField] private TransitionLibraryWindow _Window;
[SerializeField] private SelectionType _Type;
[SerializeField] private int _FromIndex = -1;
[SerializeField] private int _ToIndex = -1;
[SerializeField] private int _Version;
/// <summary>The window this selection is associated with.</summary>
public TransitionLibraryWindow Window
=> _Window;
/// <summary>The type of selected object.</summary>
public SelectionType Type
=> _Type;
/// <summary>The index of the <see cref="FromTransition"/>.</summary>
public int FromIndex
=> _FromIndex;
/// <summary>The index of the <see cref="ToTransition"/>.</summary>
public int ToIndex
=> _ToIndex;
/// <summary>The number of times this selection has been changed.</summary>
public int Version
=> _Version;
/************************************************************************************************************************/
/// <summary>The transition the current selection is coming from.</summary>
public TransitionAssetBase FromTransition { get; private set; }
/// <summary>The transition the current selection is going to.</summary>
public TransitionAssetBase ToTransition { get; private set; }
/// <summary>The <see cref="ITransition.FadeDuration"/> of the current selection.</summary>
public float FadeDuration { get; private set; }
/// <summary>The <see cref="ITransition.NormalizedStartTime"/> of the current selection.</summary>
public float NormalizedStartTime { get; private set; }
/// <summary>Does the current selection have a modified <see cref="FadeDuration"/>?</summary>
public bool HasModifier { get; private set; }
/************************************************************************************************************************/
[NonSerialized] private object _Selected;
/// <summary>The currently selected object.</summary>
public object Selected
{
get
{
Validate();
return _Selected;
}
}
/************************************************************************************************************************/
/// <summary>Deselects the current object if it isn't valid.</summary>
public bool Validate()
{
if (IsValid())
return true;
Deselect();
return false;
}
/// <summary>Is the current selection valid?</summary>
public bool IsValid()
{
if (this == null ||
_Window == null ||
Selection.activeObject != this)
return false;
var library = _Window.SourceObject;
if (library == null)
return false;
FromTransition = null;
ToTransition = null;
FadeDuration = float.NaN;
NormalizedStartTime = float.NaN;
HasModifier = false;
switch (_Type)
{
case SelectionType.Library:
name = "Transition Library";
_Selected = library;
return library != null;
case SelectionType.FromTransition:
name = "From Transition";
if (!_Window.Data.Transitions.TryGet(_FromIndex, out var transition))
return false;
FromTransition = transition;
FadeDuration = transition.TryGetFadeDuration();
NormalizedStartTime = transition.TryGetNormalizedStartTime();
_Selected = transition;
return true;
case SelectionType.ToTransition:
name = "To Transition";
if (!_Window.Data.Transitions.TryGet(_ToIndex, out transition))
return false;
ToTransition = transition;
FadeDuration = transition.TryGetFadeDuration();
NormalizedStartTime = transition.TryGetNormalizedStartTime();
_Selected = transition;
return true;
case SelectionType.Modifier:
name = "Transition Modifier";
var hasTransitions = _Window.Data.TryGetTransition(_FromIndex, out transition);
FromTransition = transition;
hasTransitions |= _Window.Data.TryGetTransition(_ToIndex, out transition);
ToTransition = transition;
if (_Window.Data.TryGetModifier(_FromIndex, _ToIndex, out var modifier))
{
HasModifier = true;
}
else if (!hasTransitions)
{
return false;
}
FadeDuration = modifier.FadeDuration;
NormalizedStartTime = modifier.NormalizedStartTime;
_Selected = modifier;
return true;
case SelectionType.Group:
name = "Transition Group";
return _Selected is TransitionGroup;
default:
return false;
}
}
/************************************************************************************************************************/
/// <summary>Sets the <see cref="Selected"/> object.</summary>
/// <remarks>
/// We can't simply set the <see cref="Selection.activeObject"/>
/// because it might not be a <see cref="UnityEngine.Object"/>
/// and if it is then we don't want the Project window to move to it.
/// <para></para>
/// So instead, we select this dummy object and <see cref="TransitionLibrarySelectionEditor"/>
/// draws a custom Inspector for the target object.
/// </remarks>
public void Select(
TransitionLibraryWindow window,
object select,
int index,
SelectionType type)
{
switch (type)
{
case SelectionType.Library:
_FromIndex = -1;
_ToIndex = -1;
break;
case SelectionType.FromTransition:
_FromIndex = index;
_ToIndex = -1;
break;
case SelectionType.ToTransition:
_FromIndex = -1;
_ToIndex = index;
break;
case SelectionType.Modifier:
if (select is TransitionModifierDefinition modifier)
{
_FromIndex = modifier.FromIndex;
_ToIndex = modifier.ToIndex;
break;
}
else
{
Deselect();
return;
}
case SelectionType.Group:
if (select is TransitionGroup group)
{
_FromIndex = index;
_ToIndex = index;
break;
}
else
{
Deselect();
return;
}
default:
Deselect();
throw new ArgumentException($"Unhandled {nameof(SelectionType)}", nameof(type));
}
_Window = window;
_Type = type;
_Selected = select;
_Version++;
Selection.activeObject = this;
Validate();
}
/************************************************************************************************************************/
/// <summary>Clears the <see cref="Selected"/> object.</summary>
public void Deselect()
{
_Window = null;
_Type = default;
_FromIndex = -1;
_ToIndex = -1;
_Selected = null;
_Version++;
if (Selection.activeObject == this)
Selection.activeObject = null;
}
/************************************************************************************************************************/
/// <summary>Handles selection changes.</summary>
public void OnSelectionChange()
{
if (Selection.activeObject == this)
return;
Deselect();
if (_Window != null)
_Window.Repaint();
}
/************************************************************************************************************************/
/// <summary>Selects this object if it contains a valid selection.</summary>
protected virtual void OnEnable()
{
if (Selected != null)
Selection.activeObject = this;
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 7d5cccea07064a74a91890855fd2aaa4
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,273 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik //
#if UNITY_EDITOR
using Animancer.TransitionLibraries;
using System;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor.TransitionLibraries
{
/// <summary>[Editor-Only]
/// A custom Inspector for <see cref="TransitionLibrarySelection"/>.
/// </summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibrarySelectionEditor
[CustomEditor(typeof(TransitionLibrarySelection), true)]
public class TransitionLibrarySelectionEditor : UnityEditor.Editor
{
/************************************************************************************************************************/
/// <summary>Casts the <see cref="UnityEditor.Editor.target"/>.</summary>
public TransitionLibrarySelection Target
=> target as TransitionLibrarySelection;
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnInspectorGUI()
{
var target = Target;
if (target == null || !target.Validate())
return;
EditorGUI.BeginChangeCheck();
switch (target.Type)
{
case TransitionLibrarySelection.SelectionType.Library:
DoNestedEditorGUI(target.Selected as TransitionLibraryAsset, "Transition Library");
break;
case TransitionLibrarySelection.SelectionType.FromTransition:
case TransitionLibrarySelection.SelectionType.ToTransition:
DoTransitionGUI(target.Selected as TransitionAssetBase);
break;
case TransitionLibrarySelection.SelectionType.Modifier:
DoModifierGUI(target, (TransitionModifierDefinition)target.Selected);
break;
case TransitionLibrarySelection.SelectionType.Group:
DoGroupGUI(target, (TransitionGroup)target.Selected);
break;
default:
target.Deselect();
break;
}
if (EditorGUI.EndChangeCheck())
target.Window.Repaint();
}
/************************************************************************************************************************/
#region Nested Editor
/************************************************************************************************************************/
[NonSerialized] private readonly CachedEditor NestedEditor = new();
[NonSerialized] private readonly CachedEditor NestedEditor2 = new();
/************************************************************************************************************************/
/// <summary>Draws the <see cref="UnityEditor.Editor"/> for the `target`.</summary>
private void DoNestedEditorGUI<T>(T target, string referenceLabel)
where T : Object
{
using (new EditorGUI.DisabledScope(true))
AnimancerGUI.DoObjectFieldGUI(referenceLabel, target, false);
var editor = NestedEditor.GetEditor(target);
if (editor != null)
editor.OnInspectorGUI();
}
/************************************************************************************************************************/
/// <summary>Cleans up any nested editors.</summary>
protected virtual void OnDestroy()
{
NestedEditor.Dispose();
NestedEditor2.Dispose();
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Transitions
/************************************************************************************************************************/
/// <summary>Draws the GUI for the `transition`.</summary>
private void DoTransitionGUI(
TransitionAssetBase transition)
{
DoTransitionNameGUI(transition);
DoNestedEditorGUI(transition, "Transition Asset");
}
/************************************************************************************************************************/
/// <summary>Draws a field for editing the name of the `transition`.</summary>
private void DoTransitionNameGUI(
TransitionAssetBase transition)
{
var isSubAsset = AssetDatabase.IsSubAsset(transition);
var isMainAsset = !isSubAsset && AssetDatabase.IsMainAsset(transition);
var label = isSubAsset
? "Sub-Asset Name"
: isMainAsset
? "File Name"
: "Name";
EditorGUI.BeginChangeCheck();
var name = TransitionModifierTableGUI.GetTransitionName(transition);
name = EditorGUILayout.DelayedTextField(label, name);
if (EditorGUI.EndChangeCheck() && transition != null)
{
transition.SetName(name);
if (isSubAsset)
{
AssetDatabase.SaveAssets();
}
else if (isMainAsset)
{
AssetDatabase.RenameAsset(
AssetDatabase.GetAssetPath(transition),
name);
}
}
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Modifiers
/************************************************************************************************************************/
private static readonly BoolPref
IsFromExpanded = new($"{nameof(TransitionLibrarySelectionEditor)}.{nameof(IsFromExpanded)}"),
IsToExpanded = new($"{nameof(TransitionLibrarySelectionEditor)}.{nameof(IsToExpanded)}");
/************************************************************************************************************************/
/// <summary>Draws the GUI for the `modifier`.</summary>
private void DoModifierGUI(
TransitionLibrarySelection selection,
TransitionModifierDefinition modifier)
{
var library = selection.Window.Data;
DoTransitionField(library, NestedEditor, IsFromExpanded, modifier.FromIndex, "From");
DoTransitionField(library, NestedEditor2, IsToExpanded, modifier.ToIndex, "To");
if (selection.Window.TryGetPage<TransitionLibraryFadeDurationsPage>(out var fadeDurations))
{
var area = AnimancerGUI.LayoutSingleLineRect();
TransitionModifierTableGUI.DoModifierValueGUI(
area,
selection.Window,
fadeDurations,
modifier.FromIndex,
modifier.ToIndex,
"Fade Duration",
false);
}
if (selection.Window.TryGetPage<TransitionLibraryStartTimesPage>(out var startTimes))
{
var area = AnimancerGUI.LayoutSingleLineRect();
TransitionModifierTableGUI.DoModifierValueGUI(
area,
selection.Window,
startTimes,
modifier.FromIndex,
modifier.ToIndex,
"Start Time",
false);
}
}
/************************************************************************************************************************/
/// <summary>Draws the GUI for a transition.</summary>
private TransitionAssetBase DoTransitionField(
TransitionLibraryDefinition library,
CachedEditor cachedEditor,
BoolPref isExpanded,
int transitionIndex,
string label)
{
library.TryGetTransition(transitionIndex, out var transition);
var area = AnimancerGUI.LayoutSingleLineRect(AnimancerGUI.SpacingMode.After);
var labelArea = area;
labelArea.width = EditorGUIUtility.labelWidth;
isExpanded.Value = EditorGUI.Foldout(labelArea, isExpanded, GUIContent.none, true);
var enabled = GUI.enabled;
GUI.enabled = false;
AnimancerGUI.DoObjectFieldGUI(area, label, transition, false);
GUI.enabled = enabled;
if (isExpanded)
{
GUILayout.BeginVertical(GUI.skin.box);
var editor = cachedEditor.GetEditor(transition);
editor.OnInspectorGUI();
GUILayout.EndVertical();
}
return transition;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Groups
/************************************************************************************************************************/
/// <summary>Draws the GUI for the `group`.</summary>
private void DoGroupGUI(
TransitionLibrarySelection selection,
TransitionGroup group)
{
group.Name = EditorGUILayout.TextField("Group Name", group.Name);
var enabled = GUI.enabled;
GUI.enabled = false;
EditorGUILayout.LabelField("Transition Count", group.TransitionIndices.Count.ToStringCached());
var transitions = selection.Window.Data.Transitions;
for (int i = 0; i < group.TransitionIndices.Count; i++)
{
var index = group.TransitionIndices[i];
if (!transitions.TryGetObject(index, out var transition))
continue;
EditorGUILayout.ObjectField(
$"Transition {i.ToStringCached()}",
transition,
typeof(TransitionAssetBase),
false);
}
GUI.enabled = enabled;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 0cfcfbc7bdddf4d4fabcf7aa15635df6
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,524 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik //
#if UNITY_EDITOR && UNITY_IMGUI
using Animancer.Editor.Previews;
using Animancer.TransitionLibraries;
using System;
using UnityEditor;
using UnityEngine;
using static Animancer.Editor.AnimancerGUI;
using Object = UnityEngine.Object;
namespace Animancer.Editor.TransitionLibraries
{
/// <summary>[Editor-Only] Custom preview for <see cref="TransitionLibrarySelection"/>.</summary>
/// <remarks>Parts of this class are based on Unity's <see cref="MeshPreview"/>.</remarks>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibrarySelectionPreview
[CustomPreview(typeof(TransitionLibrarySelection))]
public class TransitionLibrarySelectionPreview : ObjectPreview
{
/************************************************************************************************************************/
[SerializeField] private AnimancerPreviewRenderer _PreviewRenderer;
[SerializeField] private TransitionPreviewPlayer _PreviewPlayer;
[NonSerialized] private TransitionLibrarySelection _Target;
[NonSerialized] private int _TargetVersion = -1;
[NonSerialized] private readonly TransitionLibrarySelectionPreviewSpeed Speed = new();
/************************************************************************************************************************/
/// <inheritdoc/>
public override void Initialize(Object[] targets)
{
_PreviewRenderer ??= new();
_PreviewPlayer ??= new();
if (targets.Length == 1)
{
_Target = targets[0] as TransitionLibrarySelection;
if (_Target != null)
{
_TargetVersion = _Target.Version - 1;
if (_Target.Window != null)
_PreviewRenderer.PreviewObject.TrySelectBestModel(_Target.Window.Data);
CheckTarget();
}
}
base.Initialize(targets);
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override void Cleanup()
{
base.Cleanup();
_PreviewPlayer?.Dispose();
_PreviewPlayer = null;
_PreviewRenderer?.Dispose();
_PreviewRenderer = null;
}
/************************************************************************************************************************/
/// <summary>Handles changes to the target object.</summary>
private void CheckTarget()
{
if (_TargetVersion == _Target.Version)
return;
_TargetVersion = _Target.Version;
_PreviewPlayer.IsPlaying = false;
switch (_Target.Type)
{
case TransitionLibrarySelection.SelectionType.FromTransition:
_PreviewPlayer.FromTransition = _Target.FromTransition;
_PreviewPlayer.ToTransition = null;
break;
case TransitionLibrarySelection.SelectionType.ToTransition:
_PreviewPlayer.FromTransition = null;
_PreviewPlayer.ToTransition = _Target.ToTransition;
break;
case TransitionLibrarySelection.SelectionType.Modifier:
_PreviewPlayer.FromTransition = _Target.FromTransition;
_PreviewPlayer.ToTransition = _Target.ToTransition;
break;
}
}
/************************************************************************************************************************/
/// <summary>Updates the settings of the <see cref="TransitionPreviewPlayer"/>.</summary>
private void UpdatePlayerSettings()
{
_PreviewPlayer.Graph = _PreviewRenderer.PreviewObject.Graph;
_PreviewPlayer.FadeDuration = _Target.FadeDuration;
_PreviewPlayer.NormalizedStartTime = _Target.NormalizedStartTime;
_PreviewPlayer.Speed = Speed.Speed;
_PreviewPlayer.RecalculateTimeBounds();
}
/************************************************************************************************************************/
private static readonly GUIContent
Title = new("Preview");
/// <inheritdoc/>
public override GUIContent GetPreviewTitle()
=> Title;
/************************************************************************************************************************/
/// <inheritdoc/>
public override bool HasPreviewGUI()
=> _Target != null
&& _Target.Type switch
{
TransitionLibrarySelection.SelectionType.FromTransition or
TransitionLibrarySelection.SelectionType.ToTransition or
TransitionLibrarySelection.SelectionType.Modifier
=> true,
_ => false,
};
/************************************************************************************************************************/
#region Header Settings
/************************************************************************************************************************/
private static GUIStyle _ToolbarButtonStyle;
/// <inheritdoc/>
public override void OnPreviewSettings()
{
CheckTarget();
_ToolbarButtonStyle ??= new(EditorStyles.toolbarButton)
{
padding = new(),
};
var area = GUILayoutUtility.GetRect(LineHeight * 1.5f, LineHeight);
DoPlayPauseToggle(area, _ToolbarButtonStyle);
area = GUILayoutUtility.GetRect(LineHeight * 2f, LineHeight);
Speed.DoToggleGUI(area, _ToolbarButtonStyle);
}
/************************************************************************************************************************/
/// <summary>Draws a toggle to play and pause the preview.</summary>
private void DoPlayPauseToggle(Rect area, GUIStyle style)
{
if (TryUseClickEvent(area, 1) || TryUseClickEvent(area, 2))
_PreviewPlayer.CurrentTime = _PreviewPlayer.MinTime;
_PreviewPlayer.IsPlaying = AnimancerGUI.DoPlayPauseToggle(
area,
_PreviewPlayer.IsPlaying,
style,
"Left Click = Play/Pause\nRight Click = Reset Time");
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
/// <inheritdoc/>
public override void OnInteractivePreviewGUI(Rect area, GUIStyle background)
{
if (_Target == null)
return;
CheckTarget();
UpdatePlayerSettings();
DoSettingsGUI(ref area);
DoTimelineGUI(ref area);
_PreviewRenderer.DoGUI(area, background);
AnimancerPreviewObjectGUI.HandleDragAndDrop(area, _PreviewRenderer.PreviewObject);
}
/************************************************************************************************************************/
/// <summary>Draws settings for modifying the preview.</summary>
private void DoSettingsGUI(ref Rect area)
{
if (!Speed.IsOn)
return;
area.yMin += StandardSpacing;
Speed.DoSpeedSlider(ref area, EditorStyles.toolbar);
var preview = _PreviewRenderer.PreviewObject;
var height = AnimancerPreviewObjectGUI.CalculateHeight(preview);
var settingsArea = StealFromTop(ref area, height, StandardSpacing);
settingsArea = settingsArea.Expand(-StandardSpacing, 0);
GUI.Label(settingsArea, GUIContent.none, EditorStyles.toolbar);
AnimancerPreviewObjectGUI.DoModelGUI(settingsArea, preview);
}
/************************************************************************************************************************/
#region Timeline
/************************************************************************************************************************/
/// <summary>Draws the preview timeline.</summary>
private void DoTimelineGUI(ref Rect area)
{
var timelineArea = StealFromTop(ref area, EditorStyles.toolbar.fixedHeight, StandardSpacing);
EditorGUI.DrawRect(timelineArea, Grey(0.25f, 0.3f));
EditorGUI.DrawRect(new(timelineArea.x, timelineArea.yMax - 1, timelineArea.width, 1), Grey(0, 0.5f));
DoFadeDurationSliderGUI(timelineArea);
DoTimeSliderGUI(timelineArea);
}
/************************************************************************************************************************/
private static readonly int SliderHash = "Slider".GetHashCode();
/************************************************************************************************************************/
/// <summary>Draws the fade duration slider.</summary>
private void DoFadeDurationSliderGUI(Rect area)
{
if (!CalculateFadeBounds(area, out var startFadeX, out var endFadeX))
return;
switch (_Target.Type)
{
default:
return;
case TransitionLibrarySelection.SelectionType.FromTransition:
case TransitionLibrarySelection.SelectionType.ToTransition:
case TransitionLibrarySelection.SelectionType.Modifier:
break;
}
var sliderArea = area;
sliderArea.width = LineHeight * 0.5f;
sliderArea.x = endFadeX - sliderArea.width * 0.5f;
var control = new GUIControl(sliderArea, SliderHash);
switch (control.EventType)
{
case EventType.MouseDown:
if (control.TryUseMouseDown())
_PreviewPlayer.IsPlaying = false;
break;
case EventType.MouseUp:
control.TryUseMouseUp();
break;
case EventType.MouseDrag:
if (control.TryUseHotControl())
{
var x = Math.Max(startFadeX, control.Event.mousePosition.x);
var normalizedTime = area.InverseLerpUnclampedX(x);
var normalizedStartFade = area.InverseLerpUnclampedX(startFadeX);
_PreviewPlayer.NormalizedTime = normalizedTime;
var fadeDuration =
_PreviewPlayer.LerpTimeUnclamped(normalizedTime) -
_PreviewPlayer.LerpTimeUnclamped(normalizedStartFade);
var selected = _Target.Selected;
if (selected is TransitionModifierDefinition modifier)
{
_Target.Window.RecordUndo()
.SetModifier(modifier.WithFadeDuration(fadeDuration));
}
else if (selected is TransitionAssetBase transitionAsset)
{
if (fadeDuration < 0)
fadeDuration = 0;
using var serializedObject = new SerializedObject(transitionAsset);
var property = serializedObject.FindProperty(TransitionAssetBase.TransitionField);
property = property.FindPropertyRelative("_" + nameof(ITransition.FadeDuration));
property.floatValue = fadeDuration;
serializedObject.ApplyModifiedProperties();
}
_Target.Window.Repaint();
}
break;
case EventType.Repaint:
var color = AnimancerStateDrawerColors.FadeLineColor;
var showCursor = GUIUtility.hotControl == 0 || GUIUtility.hotControl == control.ID;
if (showCursor)
EditorGUIUtility.AddCursorRect(sliderArea, MouseCursor.ResizeHorizontal);
if (!showCursor || !sliderArea.Contains(control.Event.mousePosition))
color.a *= 0.5f;
EditorGUI.DrawRect(
new(endFadeX, sliderArea.y, 1, sliderArea.height - 1),
color);
break;
}
}
/************************************************************************************************************************/
/// <summary>Draws the preview time slider.</summary>
private void DoTimeSliderGUI(Rect area)
{
var control = new GUIControl(area, SliderHash);
switch (control.EventType)
{
case EventType.MouseDown:
if (control.TryUseMouseDown())
{
_ForceClampTime = true;
_DidWrapTime = false;
HandleDragTime(area, control.Event);
_ForceClampTime = control.Event.control;
if (!_ForceClampTime)
EditorGUIUtility.SetWantsMouseJumping(1);
_PreviewPlayer.IsPlaying = control.Event.clickCount > 1;
}
break;
case EventType.MouseUp:
if (control.TryUseMouseUp())
EditorGUIUtility.SetWantsMouseJumping(0);
break;
case EventType.MouseDrag:
if (control.TryUseHotControl())
HandleDragTime(area, control.Event);
break;
case EventType.Repaint:
BeginTriangles(AnimancerStateDrawerColors.FadeLineColor);
if (CalculateFadeBounds(area, out var startFadeX, out var endFadeX))
{
// Fade.
DrawLineBatched(
new(startFadeX, area.yMin + 1),
new(endFadeX, area.yMax - 1),
1);
// To.
if (endFadeX < area.xMax)
DrawLineBatched(
new(endFadeX, area.yMax - 1),
new(area.xMax, area.yMax - 1),
1);
}
// From.
if (area.xMin < startFadeX)
DrawLineBatched(
new(area.xMin, area.yMin + 1),
new(startFadeX, area.yMin + 1),
1);
var color = _PreviewPlayer.IsPlaying
? AnimancerStateDrawerColors.PlayingBarColor
: AnimancerStateDrawerColors.PausedBarColor;
color.a = 1;
var timeX = area.LerpUnclampedX(_PreviewPlayer.NormalizedTime);
GL.Color(color);
DrawLineBatched(new(timeX, area.yMin), new(timeX, area.yMax), 2);
EndTriangles();
DoTransitionLabels(area);
break;
}
}
/************************************************************************************************************************/
private bool _ForceClampTime;
private bool _DidWrapTime;
/// <summary>Draws handles drag events to control the preview time.</summary>
private void HandleDragTime(Rect area, Event currentEvent)
{
if (_ForceClampTime)
{
_PreviewPlayer.NormalizedTime = area.InverseLerpUnclampedX(currentEvent.mousePosition.x);
return;
}
var delta = currentEvent.delta.x;
var normalizedTime = _PreviewPlayer.NormalizedTime;
if (normalizedTime == 0 && !_DidWrapTime && delta > 0)
{
var x = currentEvent.mousePosition.x;
if (area.xMin > x || area.xMax < x)
return;
}
normalizedTime += delta / area.width;
if (normalizedTime >= 0 || _DidWrapTime)
{
if (normalizedTime > 1)
_DidWrapTime = true;
normalizedTime = AnimancerUtilities.Wrap01(normalizedTime);
}
else
{
normalizedTime = 0;
}
_PreviewPlayer.NormalizedTime = normalizedTime;
}
/************************************************************************************************************************/
/// <summary>Calculates the start and end pixels of the fade.</summary>
private bool CalculateFadeBounds(
Rect area,
out float startFadeX,
out float endFadeX)
{
var fadeDuration = _Target.FadeDuration;
if (!float.IsNaN(fadeDuration))
{
startFadeX = area.LerpUnclampedX(_PreviewPlayer.InverseLerpTimeUnclamped(0));
endFadeX = area.LerpUnclampedX(_PreviewPlayer.InverseLerpTimeUnclamped(fadeDuration));
if (_Target.FromTransition.IsValid())
{
if (!_Target.ToTransition.IsValid())
{
endFadeX -= startFadeX;
startFadeX = area.xMin;
}
return true;
}
else
{
if (_Target.ToTransition.IsValid())
{
return true;
}
}
}
startFadeX = area.LerpUnclampedX(_PreviewPlayer.InverseLerpTimeUnclamped(0));
endFadeX = startFadeX;
return false;
}
/************************************************************************************************************************/
/// <summary>Draws labels for the selected transitions.</summary>
private void DoTransitionLabels(Rect area)
{
area.xMin += 1;
area.xMax -= 2;
var mid = area.width * 0.5f;
var leftArea = area;
var rightArea = area;
var fromTransition = _Target.FromTransition;
var toTransition = _Target.ToTransition;
var hasFrom = fromTransition.IsValid();
var hasTo = toTransition.IsValid();
if (hasFrom && hasTo)
{
leftArea.width = mid - StandardSpacing * 0.5f;
rightArea.x = area.xMax - leftArea.width;
rightArea.width = leftArea.width;
}
if (hasFrom)
GUI.Label(leftArea, _Target.FromTransition.GetCachedName());
if (hasTo)
GUI.Label(rightArea, _Target.ToTransition.GetCachedName(), RightLabelStyle);
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 7de29fe26fe25b540987aef2f28c1ee0
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,53 @@
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 Kybernetik //
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
namespace Animancer.Editor.TransitionLibraries
{
/// <summary>[Editor-Only]
/// <see cref="ToggledSpeedSlider"/> for <see cref="TransitionLibrarySelectionPreview"/>.
/// </summary>
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.TransitionLibraries/TransitionLibrarySelectionPreviewSpeed
public class TransitionLibrarySelectionPreviewSpeed : ToggledSpeedSlider
{
/************************************************************************************************************************/
private const string
SpeedPrefKey = nameof(TransitionLibrarySelectionPreviewSpeed) + "." + nameof(Speed);
/************************************************************************************************************************/
/// <summary>Creates a new <see cref="TransitionLibrarySelectionPreviewSpeed"/>.</summary>
public TransitionLibrarySelectionPreviewSpeed()
: base(nameof(TransitionLibrarySelectionPreviewSpeed) + ".Show")
{
}
/************************************************************************************************************************/
/// <inheritdoc/>
protected override void OnSetSpeed(float speed)
{
EditorPrefs.SetFloat(SpeedPrefKey, speed);
}
/************************************************************************************************************************/
/// <inheritdoc/>
public override bool DoToggleGUI(Rect area, GUIStyle style)
{
if (float.IsNaN(Speed))
Speed = EditorPrefs.GetFloat(SpeedPrefKey, 1);
return base.DoToggleGUI(area, style);
}
/************************************************************************************************************************/
}
}
#endif

View File

@@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 29d5d1e026917374788c9f8258fc1fca
timeCreated: 1516751545
licenseType: Store
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: