AnimationTimeAttributeDrawer.cs 10 KB

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