AnimancerSettings.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. #if UNITY_EDITOR
  3. #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value.
  4. using Animancer.Units;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using UnityEditor;
  8. using UnityEngine;
  9. namespace Animancer.Editor
  10. {
  11. /// <summary>[Editor-Only] Persistent settings used by Animancer.</summary>
  12. /// <remarks>
  13. /// This asset automatically creates itself when first accessed such as when opening the
  14. /// <see cref="Animancer.Editor.TransitionPreviewWindow"/> or viewing an <see cref="AnimationTimeAttribute"/>.
  15. /// <para></para>
  16. /// The default location is <em>Assets/Plugins/Animancer/Editor</em>, but you can freely move it (and the whole
  17. /// Animancer folder) anywhere in your project.
  18. /// <para></para>
  19. /// These settings can also be accessed via the Settings in the <see cref="Tools.AnimancerToolsWindow"/>
  20. /// (<c>Window/Animation/Animancer Tools</c>).
  21. /// </remarks>
  22. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerSettings
  23. ///
  24. [HelpURL(Strings.DocsURLs.APIDocumentation + "." + nameof(Editor) + "/" + nameof(AnimancerSettings))]
  25. public class AnimancerSettings : ScriptableObject
  26. {
  27. /************************************************************************************************************************/
  28. private static AnimancerSettings _Instance;
  29. /// <summary>
  30. /// Loads an existing <see cref="AnimancerSettings"/> if there is one anywhere in your project, but otherwise
  31. /// creates a new one and saves it in the same folder as this script.
  32. /// </summary>
  33. public static AnimancerSettings Instance
  34. {
  35. get
  36. {
  37. if (_Instance != null)
  38. return _Instance;
  39. _Instance = AnimancerEditorUtilities.FindAssetOfType<AnimancerSettings>();
  40. if (_Instance != null)
  41. return _Instance;
  42. _Instance = CreateInstance<AnimancerSettings>();
  43. _Instance.name = "Animancer Settings";
  44. _Instance.hideFlags = HideFlags.DontSaveInBuild;
  45. var script = MonoScript.FromScriptableObject(_Instance);
  46. var path = AssetDatabase.GetAssetPath(script);
  47. path = Path.Combine(Path.GetDirectoryName(path), $"{_Instance.name}.asset");
  48. AssetDatabase.CreateAsset(_Instance, path);
  49. return _Instance;
  50. }
  51. }
  52. /************************************************************************************************************************/
  53. private SerializedObject _SerializedObject;
  54. /// <summary>The <see cref="SerializedProperty"/> representing the <see cref="Instance"/>.</summary>
  55. public static SerializedObject SerializedObject
  56. => Instance._SerializedObject ?? (Instance._SerializedObject = new SerializedObject(Instance));
  57. /************************************************************************************************************************/
  58. private readonly Dictionary<string, SerializedProperty>
  59. SerializedProperties = new Dictionary<string, SerializedProperty>();
  60. private static SerializedProperty GetSerializedProperty(string propertyPath)
  61. {
  62. var properties = Instance.SerializedProperties;
  63. if (!properties.TryGetValue(propertyPath, out var property))
  64. {
  65. property = SerializedObject.FindProperty(propertyPath);
  66. properties.Add(propertyPath, property);
  67. }
  68. return property;
  69. }
  70. /************************************************************************************************************************/
  71. /// <summary>Base class for groups of fields that can be serialized inside <see cref="AnimancerSettings"/>.</summary>
  72. public abstract class Group
  73. {
  74. /************************************************************************************************************************/
  75. private string _BasePropertyPath;
  76. /// <summary>[Internal] Sets the prefix for <see cref="GetSerializedProperty"/>.</summary>
  77. internal void SetBasePropertyPath(string propertyPath)
  78. {
  79. _BasePropertyPath = propertyPath + ".";
  80. }
  81. /************************************************************************************************************************/
  82. /// <summary>Returns a <see cref="SerializedProperty"/> relative to the base of this group.</summary>
  83. protected SerializedProperty GetSerializedProperty(string propertyPath)
  84. => AnimancerSettings.GetSerializedProperty(_BasePropertyPath + propertyPath);
  85. /************************************************************************************************************************/
  86. /// <summary>
  87. /// Draws a <see cref="EditorGUILayout.PropertyField(SerializedProperty, GUILayoutOption[])"/> for a
  88. /// property in this group.
  89. /// </summary>
  90. protected SerializedProperty DoPropertyField(string propertyPath)
  91. {
  92. var property = GetSerializedProperty(propertyPath);
  93. EditorGUILayout.PropertyField(property, true);
  94. return property;
  95. }
  96. /************************************************************************************************************************/
  97. }
  98. /************************************************************************************************************************/
  99. /// <summary>Initializes the serialized fields.</summary>
  100. protected virtual void OnEnable()
  101. {
  102. if (_TransitionPreviewWindow == null)
  103. _TransitionPreviewWindow = new TransitionPreviewWindow.Settings();
  104. _TransitionPreviewWindow.SetBasePropertyPath(nameof(_TransitionPreviewWindow));
  105. }
  106. /************************************************************************************************************************/
  107. /// <summary>Calls <see cref="EditorUtility.SetDirty"/> on the <see cref="Instance"/>.</summary>
  108. public static new void SetDirty() => EditorUtility.SetDirty(_Instance);
  109. /************************************************************************************************************************/
  110. [SerializeField]
  111. private TransitionPreviewWindow.Settings _TransitionPreviewWindow;
  112. /// <summary>Settings for the <see cref="TransitionPreviewWindow"/>.</summary>
  113. internal static TransitionPreviewWindow.Settings TransitionPreviewWindow => Instance._TransitionPreviewWindow;
  114. /************************************************************************************************************************/
  115. [SerializeField]
  116. private AnimationTimeAttribute.Settings _AnimationTimeFields;
  117. /// <summary>Settings for the <see cref="AnimationTimeAttribute"/>.</summary>
  118. public static AnimationTimeAttribute.Settings AnimationTimeFields => Instance._AnimationTimeFields;
  119. /************************************************************************************************************************/
  120. [SerializeField, Range(0.01f, 1)]
  121. [Tooltip("The amount of time between repaint commands when 'Display Options/Repaint Constantly' is disabled")]
  122. private float _InspectorRepaintInterval = 0.25f;
  123. /// <summary>
  124. /// The amount of time between repaint commands when
  125. /// <see cref="AnimancerPlayableDrawer.RepaintConstantly"/> is disabled.
  126. /// </summary>
  127. public static float InspectorRepaintInterval => Instance._InspectorRepaintInterval;
  128. /************************************************************************************************************************/
  129. [SerializeField]
  130. [Tooltip("The frame rate to use for new animations")]
  131. private float _NewAnimationFrameRate = 12;
  132. /// <summary>The frame rate to use for new animations.</summary>
  133. public static SerializedProperty NewAnimationFrameRate => GetSerializedProperty(nameof(_NewAnimationFrameRate));
  134. /************************************************************************************************************************/
  135. /// <summary>A custom Inspector for <see cref="AnimancerSettings"/>.</summary>
  136. [CustomEditor(typeof(AnimancerSettings), true), CanEditMultipleObjects]
  137. public class Editor : UnityEditor.Editor
  138. {
  139. /************************************************************************************************************************/
  140. /// <inheritdoc/>
  141. public override void OnInspectorGUI()
  142. {
  143. base.OnInspectorGUI();
  144. EditorGUILayout.BeginHorizontal();
  145. using (ObjectPool.Disposable.AcquireContent(out var label, "Disabled Warnings"))
  146. {
  147. EditorGUI.BeginChangeCheck();
  148. var value = EditorGUILayout.EnumFlagsField(label, Validate.PermanentlyDisabledWarnings);
  149. if (EditorGUI.EndChangeCheck())
  150. Validate.PermanentlyDisabledWarnings = (OptionalWarning)value;
  151. }
  152. if (GUILayout.Button("Help", EditorStyles.miniButton, AnimancerGUI.DontExpandWidth))
  153. Application.OpenURL(Strings.DocsURLs.OptionalWarning);
  154. EditorGUILayout.EndHorizontal();
  155. }
  156. /************************************************************************************************************************/
  157. }
  158. /************************************************************************************************************************/
  159. }
  160. }
  161. #endif