AnimationTimeAttribute.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #if UNITY_EDITOR
  3. using Animancer.Editor;
  4. using System;
  5. using UnityEditor;
  6. using UnityEngine;
  7. #endif
  8. namespace Animancer.Units
  9. {
  10. /// <summary>[Editor-Conditional] Causes a float field to display using 3 fields: Normalized, Seconds, and Frames.</summary>
  11. /// <remarks>
  12. /// Documentation: <see href="https://kybernetik.com.au/animancer/docs/manual/transitions#time-fields">Time Fields</see>
  13. /// </remarks>
  14. /// https://kybernetik.com.au/animancer/api/Animancer.Units/AnimationTimeAttribute
  15. ///
  16. [System.Diagnostics.Conditional(Strings.UnityEditor)]
  17. public sealed class AnimationTimeAttribute : UnitsAttribute
  18. {
  19. /************************************************************************************************************************/
  20. /// <summary>A unit of measurement used by the <see cref="AnimationTimeAttribute"/>.</summary>
  21. public enum Units
  22. {
  23. /// <summary>A value of 1 represents the end of the animation.</summary>
  24. Normalized = 0,
  25. /// <summary>A value of 1 represents 1 second.</summary>
  26. Seconds = 1,
  27. /// <summary>A value of 1 represents 1 frame.</summary>
  28. Frames = 2,
  29. }
  30. /// <summary>An explanation of the suffixes used in fields drawn by this attribute.</summary>
  31. public const string Tooltip = "x = Normalized, s = Seconds, f = Frame";
  32. /************************************************************************************************************************/
  33. /// <summary>Cretes a new <see cref="AnimationTimeAttribute"/>.</summary>
  34. public AnimationTimeAttribute(Units units)
  35. {
  36. #if UNITY_EDITOR
  37. SetUnits(Multipliers, DisplayConverters, (int)units);
  38. #endif
  39. }
  40. /************************************************************************************************************************/
  41. #if UNITY_EDITOR
  42. /************************************************************************************************************************/
  43. /// <summary>[Editor-Only] A converter that adds an 'x' suffix to the given number.</summary>
  44. public static readonly CompactUnitConversionCache
  45. XSuffix = new CompactUnitConversionCache("x");
  46. private static new readonly CompactUnitConversionCache[] DisplayConverters =
  47. {
  48. XSuffix,
  49. new CompactUnitConversionCache("s"),
  50. new CompactUnitConversionCache("f"),
  51. };
  52. /************************************************************************************************************************/
  53. /// <summary>[Editor-Only] The default value to be used for the next field drawn by this attribute.</summary>
  54. public static float nextDefaultValue = float.NaN;
  55. /************************************************************************************************************************/
  56. /// <inheritdoc/>
  57. protected override int GetLineCount(SerializedProperty property, GUIContent label)
  58. => EditorGUIUtility.wideMode || TransitionDrawer.Context == null ? 1 : 2;
  59. /************************************************************************************************************************/
  60. /// <inheritdoc/>
  61. public override void OnGUI(Rect area, SerializedProperty property, GUIContent label)
  62. {
  63. EditorGUI.BeginChangeCheck();
  64. var nextDefaultValue = AnimationTimeAttribute.nextDefaultValue;
  65. BeginProperty(area, property, ref label, out var value);
  66. OnGUI(area, label, ref value);
  67. EndProperty(area, property, ref value);
  68. if (EditorGUI.EndChangeCheck())
  69. {
  70. TransitionPreviewWindow.PreviewNormalizedTime =
  71. GetDisplayValue(value, nextDefaultValue) * Multipliers[(int)Units.Normalized];
  72. }
  73. }
  74. /************************************************************************************************************************/
  75. /// <summary>[Editor-Only] Draws the GUI for this attribute.</summary>
  76. public void OnGUI(Rect area, GUIContent label, ref float value)
  77. {
  78. var context = TransitionDrawer.Context;
  79. if (context == null)
  80. {
  81. value = DoSpecialFloatField(area, label, value, DisplayConverters[UnitIndex]);
  82. goto Return;
  83. }
  84. var length = context.MaximumDuration;
  85. if (length <= 0)
  86. length = float.NaN;
  87. AnimancerUtilities.TryGetFrameRate(context.Transition, out var frameRate);
  88. var multipliers = CalculateMultipliers(length, frameRate);
  89. if (multipliers == null)
  90. {
  91. EditorGUI.LabelField(area, label.text, $"Invalid {nameof(Validate)}.{nameof(Validate.Value)}");
  92. goto Return;
  93. }
  94. DoPreviewTimeButton(ref area, ref value, context.Transition, multipliers);
  95. IsOptional = !float.IsNaN(nextDefaultValue);
  96. DefaultValue = nextDefaultValue;
  97. DoFieldGUI(area, label, ref value);
  98. Return:
  99. nextDefaultValue = float.NaN;
  100. }
  101. /************************************************************************************************************************/
  102. private static new readonly float[] Multipliers = new float[3];
  103. private float[] CalculateMultipliers(float length, float frameRate)
  104. {
  105. switch ((Units)UnitIndex)
  106. {
  107. case Units.Normalized:
  108. Multipliers[(int)Units.Normalized] = 1;
  109. Multipliers[(int)Units.Seconds] = length;
  110. Multipliers[(int)Units.Frames] = length * frameRate;
  111. break;
  112. case Units.Seconds:
  113. Multipliers[(int)Units.Normalized] = 1f / length;
  114. Multipliers[(int)Units.Seconds] = 1;
  115. Multipliers[(int)Units.Frames] = frameRate;
  116. break;
  117. case Units.Frames:
  118. Multipliers[(int)Units.Normalized] = 1f / length / frameRate;
  119. Multipliers[(int)Units.Seconds] = 1f / frameRate;
  120. Multipliers[(int)Units.Frames] = 1;
  121. break;
  122. default:
  123. return null;
  124. }
  125. var settings = AnimancerSettings.AnimationTimeFields;
  126. ApplyVisibilitySetting(settings.showNormalized, Units.Normalized);
  127. ApplyVisibilitySetting(settings.showSeconds, Units.Seconds);
  128. ApplyVisibilitySetting(settings.showFrames, Units.Frames);
  129. void ApplyVisibilitySetting(bool show, Units setting)
  130. {
  131. if (show)
  132. return;
  133. var index = (int)setting;
  134. if (UnitIndex != index)
  135. Multipliers[index] = float.NaN;
  136. }
  137. return Multipliers;
  138. }
  139. /************************************************************************************************************************/
  140. private void DoPreviewTimeButton(ref Rect area, ref float value, ITransitionDetailed transition,
  141. float[] multipliers)
  142. {
  143. if (!TransitionPreviewWindow.IsPreviewingCurrentProperty())
  144. return;
  145. var previewTime = TransitionPreviewWindow.PreviewNormalizedTime;
  146. const string Tooltip =
  147. "• Left Click = preview the current value of this field." +
  148. "\n• Right Click = set this field to use the current preview time.";
  149. var displayValue = GetDisplayValue(value, nextDefaultValue);
  150. var multiplier = multipliers[(int)Units.Normalized];
  151. displayValue *= multiplier;
  152. var isCurrent = Mathf.Approximately(displayValue, previewTime);
  153. var buttonArea = area;
  154. if (TransitionDrawer.DoPreviewButtonGUI(ref buttonArea, isCurrent, Tooltip))
  155. {
  156. if (Event.current.button != 1)
  157. TransitionPreviewWindow.PreviewNormalizedTime = displayValue;
  158. else
  159. value = previewTime / multiplier;
  160. }
  161. // Only steal the button area for single line fields.
  162. if (area.height <= LineHeight)
  163. area = buttonArea;
  164. }
  165. /************************************************************************************************************************/
  166. /// <summary>[Editor-Only] Options to determine how <see cref="AnimationTimeAttribute"/> displays.</summary>
  167. [Serializable]
  168. public class Settings
  169. {
  170. /************************************************************************************************************************/
  171. /// <summary>Should time fields show approximations if the value is too long for the GUI?</summary>
  172. /// <remarks>This setting is used by <see cref="CompactUnitConversionCache"/>.</remarks>
  173. [Tooltip("Should time fields show approximations if the value is too long for the GUI?" +
  174. " For example, '1.111111' could instead show '1.111~'.")]
  175. public bool showApproximations = true;
  176. /// <summary>Should the <see cref="Units.Normalized"/> field be shown?</summary>
  177. /// <remarks>This setting is ignored for fields which directly store the normalized value.</remarks>
  178. [Tooltip("Should the " + nameof(Units.Normalized) + " field be shown?")]
  179. public bool showNormalized = true;
  180. /// <summary>Should the <see cref="Units.Seconds"/> field be shown?</summary>
  181. /// <remarks>This setting is ignored for fields which directly store the seconds value.</remarks>
  182. [Tooltip("Should the " + nameof(Units.Seconds) + " field be shown?")]
  183. public bool showSeconds = true;
  184. /// <summary>Should the <see cref="Units.Frames"/> field be shown?</summary>
  185. /// <remarks>This setting is ignored for fields which directly store the frame value.</remarks>
  186. [Tooltip("Should the " + nameof(Units.Frames) + " field be shown?")]
  187. public bool showFrames = true;
  188. /************************************************************************************************************************/
  189. }
  190. /************************************************************************************************************************/
  191. #endif
  192. /************************************************************************************************************************/
  193. }
  194. }