ICharacterRoot.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2022 Kybernetik //
  2. using UnityEngine;
  3. namespace Animancer
  4. {
  5. /// <summary>
  6. /// Interface for components to indicate which <see cref="GameObject"/> is the root of a character when
  7. /// <see cref="Editor.AnimancerEditorUtilities.FindRoot(GameObject)"/> is called.
  8. /// </summary>
  9. /// https://kybernetik.com.au/animancer/api/Animancer/ICharacterRoot
  10. ///
  11. public interface ICharacterRoot
  12. {
  13. /************************************************************************************************************************/
  14. #pragma warning disable IDE1006 // Naming Styles.
  15. /************************************************************************************************************************/
  16. /// <summary>
  17. /// The <see cref="Transform"/> to search for <see cref="AnimationClip"/>s beneath.
  18. /// </summary>
  19. ///
  20. /// <example>
  21. /// Implementing this interface in a <see cref="MonoBehaviour"/> will automatically inherit this property so
  22. /// you do not need to do anything else:
  23. /// <para></para><code>
  24. /// public class MyComponent : MonoBehaviour, IAnimancerRoot
  25. /// {
  26. /// }
  27. /// </code>
  28. /// But if you want to have your script point to a different object as the root, you can explicitly implement
  29. /// this property:
  30. /// <para></para><code>
  31. /// public class MyComponent : MonoBehaviour, IAnimancerRoot
  32. /// {
  33. /// Transform IAnimancerRoot.transform => ???;
  34. /// }
  35. /// </code></example>
  36. Transform transform { get; }
  37. /************************************************************************************************************************/
  38. #pragma warning restore IDE1006 // Naming Styles.
  39. /************************************************************************************************************************/
  40. }
  41. }
  42. /************************************************************************************************************************/
  43. #if UNITY_EDITOR
  44. /************************************************************************************************************************/
  45. namespace Animancer.Editor
  46. {
  47. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerEditorUtilities
  48. partial class AnimancerEditorUtilities
  49. {
  50. /************************************************************************************************************************/
  51. /// <summary>Takes a `gameObject` and returns the root <see cref="Transform"/> of the character it is part of.</summary>
  52. ///
  53. /// <remarks>
  54. /// This method first searches all parents for an <see cref="ICharacterRoot"/>. If it finds one, it returns the
  55. /// <see cref="ICharacterRoot.transform"/>.
  56. /// <para></para>
  57. /// Otherwise, if the object is part of a prefab then it returns the root of that prefab instance.
  58. /// <para></para>
  59. /// Otherwise, it counts the number of Animators in the children of the `gameObject` then does
  60. /// the same for each parent. If it finds a parent with a different number of child Animators, it
  61. /// assumes that object is the parent of multiple characters and returns the previous parent as the root.
  62. /// </remarks>
  63. ///
  64. /// <example>
  65. /// <h2>Simple Hierarchy</h2>
  66. /// <code>
  67. /// - Character - Rigidbody, etc.
  68. /// - Model - Animator, AnimancerComponent
  69. /// - States - Various components which reference the AnimationClips they will play
  70. /// </code>
  71. /// Passing the <c>Model</c> into this method will return the <c>Character</c> because it has the same
  72. /// number of Animator components in its children.
  73. ///
  74. /// <h2>Shared Hierarchy</h2>
  75. /// <code>
  76. /// - Characters - Empty object used to group all characters
  77. /// - Character - Rigidbody, etc.
  78. /// - Model - Animator, AnimancerComponent
  79. /// - States - Various components which reference the AnimationClips they will play
  80. /// - Another Character
  81. /// - Model
  82. /// - States
  83. /// </code>
  84. /// <list type="bullet">
  85. /// <item><c>Model</c> has one Animator and no more in its children.</item>
  86. /// <item>And <c>Character</c> has one Animator in its children (the same one).</item>
  87. /// <item>But <c>Characters</c> has two Animators in its children (one on each character).</item>
  88. /// </list>
  89. /// So it picks the <c>Character</c> as the root.
  90. ///
  91. /// <h2>Complex Hierarchy</h2>
  92. /// <code>
  93. /// - Character - Rigidbody, etc.
  94. /// - Model - Animator, AnimancerComponent
  95. /// - States - Various components which reference the AnimationClips they will play
  96. /// - Another Model - Animator (maybe the character is holding a gun which has a reload animation)
  97. /// </code>
  98. /// In this case, the automatic system would see that the <c>Character</c> already has more child
  99. /// <see cref="Animator"/>s than the selected <c>Model</c> so it would only return the <c>Model</c> itself.
  100. /// This can be fixed by making any of the scripts on the <c>Character</c> implement <see cref="ICharacterRoot"/>
  101. /// to tell the system which object you want it to use as the root.
  102. /// </example>
  103. public static Transform FindRoot(GameObject gameObject)
  104. {
  105. var root = gameObject.GetComponentInParent<ICharacterRoot>();
  106. if (root != null)
  107. return root.transform;
  108. #if UNITY_EDITOR
  109. var path = UnityEditor.AssetDatabase.GetAssetPath(gameObject);
  110. if (!string.IsNullOrEmpty(path))
  111. return gameObject.transform.root;
  112. var status = UnityEditor.PrefabUtility.GetPrefabInstanceStatus(gameObject);
  113. if (status != UnityEditor.PrefabInstanceStatus.NotAPrefab)
  114. {
  115. gameObject = UnityEditor.PrefabUtility.GetOutermostPrefabInstanceRoot(gameObject);
  116. return gameObject.transform;
  117. }
  118. #endif
  119. var animators = ObjectPool.AcquireList<Animator>();
  120. gameObject.GetComponentsInChildren(true, animators);
  121. var animatorCount = animators.Count;
  122. var parent = gameObject.transform;
  123. while (parent.parent != null)
  124. {
  125. animators.Clear();
  126. parent.parent.GetComponentsInChildren(true, animators);
  127. if (animatorCount == 0)
  128. animatorCount = animators.Count;
  129. else if (animatorCount != animators.Count)
  130. break;
  131. parent = parent.parent;
  132. }
  133. ObjectPool.Release(animators);
  134. return parent;
  135. }
  136. /************************************************************************************************************************/
  137. /// <summary>
  138. /// Calls <see cref="FindRoot(GameObject)"/> if the specified `obj` is a <see cref="GameObject"/> or
  139. /// <see cref="Component"/>.
  140. /// </summary>
  141. public static Transform FindRoot(Object obj)
  142. {
  143. if (obj is ICharacterRoot iRoot)
  144. return iRoot.transform;
  145. return TryGetGameObject(obj, out var gameObject) ? FindRoot(gameObject) : null;
  146. }
  147. /************************************************************************************************************************/
  148. /// <summary>Outputs the <see cref="GameObject"/> assignated with the `obj` and returns true if it exists.</summary>
  149. /// <remarks>
  150. /// If the `obj` is a <see cref="GameObject"/> it is used as the result.
  151. /// <para></para>
  152. /// Or if the `obj` is a <see cref="Component"/> then its <see cref="Component.gameObject"/> is used as the result.
  153. /// </remarks>
  154. public static bool TryGetGameObject(Object obj, out GameObject gameObject)
  155. {
  156. if (obj is GameObject go)
  157. {
  158. gameObject = go;
  159. return true;
  160. }
  161. if (obj is Component component)
  162. {
  163. gameObject = component.gameObject;
  164. return true;
  165. }
  166. gameObject = null;
  167. return false;
  168. }
  169. /************************************************************************************************************************/
  170. }
  171. }
  172. /************************************************************************************************************************/
  173. #endif
  174. /************************************************************************************************************************/