ICharacterRoot.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 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="AnimancerUtilities.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. /// <remarks>
  21. /// <strong>Example:</strong>
  22. /// Implementing this interface in a <see cref="MonoBehaviour"/>
  23. /// will automatically inherit this property so you don't need to do anything else:
  24. /// <para></para><code>
  25. /// public class MyComponent : MonoBehaviour, ICharacterRoot
  26. /// {
  27. /// }
  28. /// </code>
  29. /// But if you want to have your script point to a different object as the root,
  30. /// you can explicitly implement this property:
  31. /// <para></para><code>
  32. /// public class MyComponent : MonoBehaviour, ICharacterRoot
  33. /// {
  34. /// Transform ICharacterRoot.transform => ???;
  35. /// }
  36. /// </code></remarks>
  37. Transform transform { get; }
  38. /************************************************************************************************************************/
  39. #pragma warning restore IDE1006 // Naming Styles.
  40. /************************************************************************************************************************/
  41. }
  42. /************************************************************************************************************************/
  43. #if UNITY_EDITOR
  44. /************************************************************************************************************************/
  45. /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerUtilities
  46. public static partial class AnimancerUtilities
  47. {
  48. /************************************************************************************************************************/
  49. /// <summary>[Editor-Only]
  50. /// Takes a `gameObject` and returns the root <see cref="Transform"/> of the character it is part of.
  51. /// </summary>
  52. ///
  53. /// <remarks>
  54. /// This method first searches all parents for a component implementing <see cref="ICharacterRoot"/>.
  55. /// If it finds one, it returns the <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 = ListPool.Acquire<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. ListPool.Release(animators);
  134. return parent;
  135. }
  136. /************************************************************************************************************************/
  137. /// <summary>[Editor-Only]
  138. /// Calls <see cref="FindRoot(GameObject)"/> if the specified `obj` is a
  139. /// <see cref="GameObject"/> or <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)
  146. ? FindRoot(gameObject)
  147. : null;
  148. }
  149. /************************************************************************************************************************/
  150. /// <summary>[Editor-Only]
  151. /// Outputs the <see cref="GameObject"/> assignated with the `obj` and returns true if it exists.
  152. /// </summary>
  153. /// <remarks>
  154. /// If the `obj` is a <see cref="GameObject"/> it is used as the result.
  155. /// <para></para>
  156. /// Or if the `obj` is a <see cref="Component"/> then its <see cref="Component.gameObject"/>
  157. /// is used as the result.
  158. /// </remarks>
  159. public static bool TryGetGameObject(Object obj, out GameObject gameObject)
  160. {
  161. if (obj is GameObject go)
  162. {
  163. gameObject = go;
  164. return true;
  165. }
  166. if (obj is Component component)
  167. {
  168. gameObject = component.gameObject;
  169. return true;
  170. }
  171. gameObject = null;
  172. return false;
  173. }
  174. /************************************************************************************************************************/
  175. }
  176. /************************************************************************************************************************/
  177. #endif
  178. /************************************************************************************************************************/
  179. }