PlayableAssetTransitionDrawer.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR && UNITY_IMGUI
  3. using System;
  4. using System.Collections.Generic;
  5. using UnityEditor;
  6. using UnityEngine;
  7. using UnityEngine.Playables;
  8. using static Animancer.Editor.AnimancerGUI;
  9. using Object = UnityEngine.Object;
  10. namespace Animancer.Editor
  11. {
  12. /// <inheritdoc/>
  13. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/PlayableAssetTransitionDrawer
  14. [CustomPropertyDrawer(typeof(PlayableAssetTransition), true)]
  15. public class PlayableAssetTransitionDrawer : TransitionDrawer
  16. {
  17. /************************************************************************************************************************/
  18. /// <summary>Creates a new <see cref="PlayableAssetTransitionDrawer"/>.</summary>
  19. public PlayableAssetTransitionDrawer()
  20. : base(PlayableAssetTransition.AssetField)
  21. { }
  22. /************************************************************************************************************************/
  23. /// <inheritdoc/>
  24. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  25. {
  26. _CurrentAsset = null;
  27. var height = base.GetPropertyHeight(property, label);
  28. if (property.isExpanded)
  29. {
  30. var bindings = property.FindPropertyRelative(PlayableAssetTransition.BindingsField);
  31. if (bindings != null)
  32. {
  33. bindings.isExpanded = true;
  34. height -= StandardSpacing + LineHeight;
  35. }
  36. }
  37. return height;
  38. }
  39. /************************************************************************************************************************/
  40. private PlayableAsset _CurrentAsset;
  41. /// <inheritdoc/>
  42. protected override void DoMainPropertyGUI(
  43. Rect area,
  44. out Rect labelArea,
  45. SerializedProperty rootProperty,
  46. SerializedProperty mainProperty)
  47. {
  48. _CurrentAsset = mainProperty.objectReferenceValue as PlayableAsset;
  49. base.DoMainPropertyGUI(area, out labelArea, rootProperty, mainProperty);
  50. }
  51. /// <inheritdoc/>
  52. public override void OnGUI(Rect area, SerializedProperty property, GUIContent label)
  53. {
  54. base.OnGUI(area, property, label);
  55. _CurrentAsset = null;
  56. }
  57. /// <inheritdoc/>
  58. protected override void DoChildPropertyGUI(
  59. ref Rect area,
  60. SerializedProperty rootProperty,
  61. SerializedProperty property,
  62. GUIContent label)
  63. {
  64. var path = property.propertyPath;
  65. if (path.EndsWith($".{PlayableAssetTransition.BindingsField}"))
  66. {
  67. DoBindingsGUI(ref area, property, label);
  68. return;
  69. }
  70. base.DoChildPropertyGUI(ref area, rootProperty, property, label);
  71. }
  72. /************************************************************************************************************************/
  73. private void DoBindingsGUI(
  74. ref Rect area,
  75. SerializedProperty property,
  76. GUIContent label)
  77. {
  78. var outputCount = GetOutputCount(out var outputEnumerator, out var firstBindingIsAnimation);
  79. // Bindings.
  80. property.Next(true);
  81. // Array.
  82. property.Next(true);
  83. // Array Size.
  84. DoBindingsCountGUI(area, property, label, outputCount, firstBindingIsAnimation, out var bindingCount);
  85. EditorGUI.indentLevel++;
  86. for (int i = 0; i < bindingCount; i++)
  87. {
  88. NextVerticalArea(ref area);
  89. if (!property.Next(false))
  90. {
  91. EditorGUI.LabelField(area, "Binding Count Mismatch");
  92. break;
  93. }
  94. // First Array Item.
  95. if (outputEnumerator != null && outputEnumerator.MoveNext())
  96. {
  97. DoBindingGUI(area, property, label, outputEnumerator, i);
  98. }
  99. else
  100. {
  101. var color = GUI.color;
  102. GUI.color = WarningFieldColor;
  103. EditorGUI.PropertyField(area, property, false);
  104. GUI.color = color;
  105. }
  106. }
  107. EditorGUI.indentLevel--;
  108. }
  109. /************************************************************************************************************************/
  110. private int GetOutputCount(
  111. out IEnumerator<PlayableBinding> outputEnumerator,
  112. out bool firstBindingIsAnimation)
  113. {
  114. var outputCount = 0;
  115. firstBindingIsAnimation = false;
  116. if (_CurrentAsset != null)
  117. {
  118. var outputs = _CurrentAsset.outputs;
  119. _CurrentAsset = null;
  120. outputEnumerator = outputs.GetEnumerator();
  121. while (outputEnumerator.MoveNext())
  122. {
  123. PlayableAssetState.GetBindingDetails(
  124. outputEnumerator.Current, out var _, out var _, out var isMarkers);
  125. if (isMarkers)
  126. continue;
  127. if (outputCount == 0 && outputEnumerator.Current.outputTargetType == typeof(Animator))
  128. firstBindingIsAnimation = true;
  129. outputCount++;
  130. }
  131. outputEnumerator = outputs.GetEnumerator();
  132. }
  133. else outputEnumerator = null;
  134. return outputCount;
  135. }
  136. /************************************************************************************************************************/
  137. private void DoBindingsCountGUI(
  138. Rect area,
  139. SerializedProperty property,
  140. GUIContent label,
  141. int outputCount,
  142. bool firstBindingIsAnimation,
  143. out int bindingCount)
  144. {
  145. var color = GUI.color;
  146. var sizeArea = area;
  147. bindingCount = property.intValue;
  148. // Button to fix the number of bindings in the array.
  149. if (bindingCount != outputCount && !(bindingCount == 0 && outputCount == 1 && firstBindingIsAnimation))
  150. {
  151. GUI.color = WarningFieldColor;
  152. var labelText = label.text;
  153. var style = MiniButtonStyle;
  154. var countLabel = outputCount.ToStringCached();
  155. var fixSizeWidth = style.CalculateWidth(countLabel);
  156. var fixSizeArea = StealFromRight(
  157. ref sizeArea, fixSizeWidth, StandardSpacing);
  158. if (GUI.Button(fixSizeArea, countLabel, style))
  159. property.intValue = bindingCount = outputCount;
  160. label.text = labelText;
  161. }
  162. EditorGUI.BeginChangeCheck();
  163. EditorGUI.PropertyField(sizeArea, property, label, false);
  164. if (EditorGUI.EndChangeCheck())
  165. bindingCount = property.intValue;
  166. GUI.color = color;
  167. }
  168. /************************************************************************************************************************/
  169. private void DoBindingGUI(
  170. Rect area,
  171. SerializedProperty property,
  172. GUIContent label,
  173. IEnumerator<PlayableBinding> outputEnumerator,
  174. int trackIndex)
  175. {
  176. CheckIfSkip:
  177. PlayableAssetState.GetBindingDetails(
  178. outputEnumerator.Current,
  179. out var name,
  180. out var bindingType,
  181. out var isMarkers);
  182. if (isMarkers)
  183. {
  184. outputEnumerator.MoveNext();
  185. goto CheckIfSkip;
  186. }
  187. label.text = name;
  188. var targetObject = property.serializedObject.targetObject;
  189. var allowSceneObjects =
  190. targetObject != null &&
  191. !EditorUtility.IsPersistent(targetObject);
  192. label = EditorGUI.BeginProperty(area, label, property);
  193. var fieldArea = area;
  194. var obj = property.objectReferenceValue;
  195. var objExists = obj != null;
  196. if (objExists)
  197. DoRemoveButtonIfNecessary(ref fieldArea, property, trackIndex, ref bindingType, ref obj);
  198. if (bindingType != null || objExists)
  199. {
  200. property.objectReferenceValue =
  201. EditorGUI.ObjectField(fieldArea, label, obj, bindingType, allowSceneObjects);
  202. }
  203. else
  204. {
  205. EditorGUI.LabelField(fieldArea, label);
  206. }
  207. EditorGUI.EndProperty();
  208. }
  209. /************************************************************************************************************************/
  210. private static void DoRemoveButtonIfNecessary(
  211. ref Rect area,
  212. SerializedProperty property,
  213. int trackIndex,
  214. ref Type bindingType,
  215. ref Object obj)
  216. {
  217. if (trackIndex == 0 && bindingType == typeof(Animator))
  218. {
  219. DoRemoveButton(ref area, property, ref obj,
  220. "This Animation Track is the first Track" +
  221. " so it will automatically control the Animancer output" +
  222. " and likely doesn't need a binding.");
  223. }
  224. else if (bindingType == null)
  225. {
  226. DoRemoveButton(ref area, property, ref obj,
  227. "This Track doesn't need a binding.");
  228. bindingType = typeof(Object);
  229. }
  230. else if (!bindingType.IsAssignableFrom(obj.GetType()))
  231. {
  232. DoRemoveButton(ref area, property, ref obj,
  233. "This binding has the wrong type for this Track.");
  234. }
  235. }
  236. /************************************************************************************************************************/
  237. private static void DoRemoveButton(
  238. ref Rect area,
  239. SerializedProperty property,
  240. ref Object obj,
  241. string tooltip)
  242. {
  243. GUI.color = WarningFieldColor;
  244. var removeArea = StealFromRight(
  245. ref area,
  246. area.height,
  247. StandardSpacing);
  248. if (GUI.Button(
  249. removeArea,
  250. AnimancerIcons.ClearIcon(tooltip),
  251. NoPaddingButtonStyle))
  252. property.objectReferenceValue = obj = null;
  253. }
  254. /************************************************************************************************************************/
  255. }
  256. }
  257. #endif