AnimancerPreviewObjectGUI.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR && UNITY_IMGUI
  3. using System.Collections.Generic;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using static Animancer.Editor.AnimancerGUI;
  7. namespace Animancer.Editor.Previews
  8. {
  9. /// <summary>[Editor-Only] GUI utilities for <see cref="AnimancerPreviewObject"/>.</summary>
  10. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/AnimancerPreviewObjectGUI
  11. public static class AnimancerPreviewObjectGUI
  12. {
  13. /************************************************************************************************************************/
  14. /// <summary>Calculates the pixel height required to draw the `preview`.</summary>
  15. public static float CalculateHeight(AnimancerPreviewObject preview)
  16. {
  17. var lines = 1;
  18. var instanceAnimators = preview.InstanceAnimators;
  19. if (instanceAnimators != null &&
  20. instanceAnimators.Length > 1)
  21. lines++;
  22. return AnimancerGUI.CalculateHeight(lines);
  23. }
  24. /************************************************************************************************************************/
  25. /// <summary>Draws the model selection GUI.</summary>
  26. public static void DoModelGUI(AnimancerPreviewObject preview)
  27. {
  28. var area = LayoutRect(CalculateHeight(preview));
  29. DoModelGUI(area, preview);
  30. }
  31. /************************************************************************************************************************/
  32. /// <summary>Draws the model selection GUI.</summary>
  33. public static void DoModelGUI(Rect area, AnimancerPreviewObject preview)
  34. {
  35. var root = preview.OriginalObject;
  36. var model = root != null ? root.gameObject : null;
  37. EditorGUI.BeginChangeCheck();
  38. var warning = GetModelWarning(model, preview);
  39. var color = GUI.color;
  40. if (warning != null)
  41. GUI.color = WarningFieldColor;
  42. using (var label = PooledGUIContent.Acquire("Model"))
  43. {
  44. var objectFieldArea = StealLineFromTop(ref area);
  45. if (DoDropdownObjectFieldGUI(objectFieldArea, label, true, ref model))
  46. {
  47. var menu = new GenericMenu();
  48. menu.AddItem(
  49. new("Default Humanoid"),
  50. model != null && model == TransitionPreviewSettings.GetOrCreateDefaultHumanoid(null),
  51. () => preview.OriginalObject
  52. = TransitionPreviewSettings.GetOrCreateDefaultHumanoid(preview.InstanceRoot).transform);
  53. menu.AddItem(
  54. new("Default Sprite"),
  55. model != null && model == TransitionPreviewSettings.GetDefaultSpriteIfAlreadyCreated(),
  56. () => preview.OriginalObject
  57. = TransitionPreviewSettings.GetOrCreateDefaultSprite(preview.InstanceRoot).transform);
  58. var persistentModels = TransitionPreviewSettings.Models;
  59. var temporaryModels = TemporarySettings.PreviewModels;
  60. if (persistentModels.Count == 0 && temporaryModels.Count == 0)
  61. {
  62. menu.AddDisabledItem(new("No model prefabs have been used yet"));
  63. }
  64. else
  65. {
  66. AddModelSelectionFunctions(menu, preview, persistentModels, model);
  67. AddModelSelectionFunctions(menu, preview, temporaryModels, model);
  68. }
  69. menu.ShowAsContext();
  70. }
  71. }
  72. GUI.color = color;
  73. if (EditorGUI.EndChangeCheck())
  74. preview.OriginalObject = model != null ? model.transform : null;
  75. if (warning != null)
  76. EditorGUILayout.HelpBox(warning, MessageType.Warning, true);
  77. DoAnimatorSelectorGUI(preview);
  78. }
  79. /************************************************************************************************************************/
  80. /// <summary>Adds menu functions for selecting each of the `models`.</summary>
  81. private static void AddModelSelectionFunctions(
  82. GenericMenu menu,
  83. AnimancerPreviewObject preview,
  84. List<GameObject> models,
  85. GameObject selected)
  86. {
  87. for (int i = models.Count - 1; i >= 0; i--)
  88. {
  89. var model = models[i];
  90. var path = AssetDatabase.GetAssetPath(model);
  91. if (!string.IsNullOrEmpty(path))
  92. path = path.Replace('/', '\\');
  93. else
  94. path = model.name;
  95. menu.AddItem(new(path), model == selected,
  96. () => preview.OriginalObject = model.transform);
  97. }
  98. }
  99. /************************************************************************************************************************/
  100. /// <summary>Returns a warning about the selected model or <c>null</c>.</summary>
  101. private static string GetModelWarning(
  102. GameObject model,
  103. AnimancerPreviewObject preview)
  104. {
  105. if (model == null)
  106. return "No Model is selected so nothing can be previewed.";
  107. if (preview.SelectedInstanceAnimator == null)
  108. return "The selected Model has no Animator component.";
  109. return null;
  110. }
  111. /************************************************************************************************************************/
  112. /// <summary>
  113. /// Draws a button for selecting which <see cref="Animator"/> to control if there's more than one.
  114. /// </summary>
  115. private static void DoAnimatorSelectorGUI(AnimancerPreviewObject preview)
  116. {
  117. var instanceAnimators = preview.InstanceAnimators;
  118. if (instanceAnimators == null ||
  119. instanceAnimators.Length <= 1)
  120. return;
  121. var area = LayoutSingleLineRect(SpacingMode.After);
  122. var labelArea = StealFromLeft(ref area, EditorGUIUtility.labelWidth, StandardSpacing);
  123. GUI.Label(labelArea, nameof(Animator));
  124. var selectedAnimator = preview.SelectedInstanceAnimator;
  125. using (var label = PooledGUIContent.Acquire(
  126. selectedAnimator != null ? selectedAnimator.name : "None"))
  127. {
  128. var clicked = EditorGUI.DropdownButton(area, label, FocusType.Passive);
  129. if (!clicked)
  130. return;
  131. var menu = new GenericMenu();
  132. for (int i = 0; i < instanceAnimators.Length; i++)
  133. {
  134. var animator = instanceAnimators[i];
  135. var index = i;
  136. menu.AddItem(new(animator.name), animator == selectedAnimator, () =>
  137. {
  138. preview.SetSelectedAnimator(index);
  139. });
  140. }
  141. menu.ShowAsContext();
  142. }
  143. }
  144. /************************************************************************************************************************/
  145. private static DragAndDropHandler<GameObject> _ModelDropHandler;
  146. private static AnimancerPreviewObject _ModelDropPreview;
  147. /// <summary>Handles drag and drop events for preview models.</summary>
  148. public static void HandleDragAndDrop(Rect area, AnimancerPreviewObject preview)
  149. {
  150. _ModelDropPreview = preview;
  151. _ModelDropHandler ??= (gameObject, isDrop) =>
  152. {
  153. if (!gameObject.TryGetComponent<Animator>(out _))
  154. return false;
  155. if (isDrop)
  156. _ModelDropPreview.OriginalObject = gameObject.transform;
  157. return true;
  158. };
  159. _ModelDropPreview = null;
  160. }
  161. /************************************************************************************************************************/
  162. }
  163. }
  164. #endif