123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
- using UnityEngine;
- namespace Animancer
- {
- /// <summary>
- /// Interface for components to indicate which <see cref="GameObject"/> is the root of a character when
- /// <see cref="AnimancerUtilities.FindRoot(GameObject)"/> is called.
- /// </summary>
- /// https://kybernetik.com.au/animancer/api/Animancer/ICharacterRoot
- ///
- public interface ICharacterRoot
- {
- /************************************************************************************************************************/
- #pragma warning disable IDE1006 // Naming Styles.
- /************************************************************************************************************************/
- /// <summary>
- /// The <see cref="Transform"/> to search for <see cref="AnimationClip"/>s beneath.
- /// </summary>
- ///
- /// <remarks>
- /// <strong>Example:</strong>
- /// Implementing this interface in a <see cref="MonoBehaviour"/>
- /// will automatically inherit this property so you don't need to do anything else:
- /// <para></para><code>
- /// public class MyComponent : MonoBehaviour, ICharacterRoot
- /// {
- /// }
- /// </code>
- /// But if you want to have your script point to a different object as the root,
- /// you can explicitly implement this property:
- /// <para></para><code>
- /// public class MyComponent : MonoBehaviour, ICharacterRoot
- /// {
- /// Transform ICharacterRoot.transform => ???;
- /// }
- /// </code></remarks>
- Transform transform { get; }
- /************************************************************************************************************************/
- #pragma warning restore IDE1006 // Naming Styles.
- /************************************************************************************************************************/
- }
- /************************************************************************************************************************/
- #if UNITY_EDITOR
- /************************************************************************************************************************/
- /// https://kybernetik.com.au/animancer/api/Animancer/AnimancerUtilities
- public static partial class AnimancerUtilities
- {
- /************************************************************************************************************************/
- /// <summary>[Editor-Only]
- /// Takes a `gameObject` and returns the root <see cref="Transform"/> of the character it is part of.
- /// </summary>
- ///
- /// <remarks>
- /// This method first searches all parents for a component implementing <see cref="ICharacterRoot"/>.
- /// If it finds one, it returns the <see cref="ICharacterRoot.transform"/>.
- /// <para></para>
- /// Otherwise, if the object is part of a prefab then it returns the root of that prefab instance.
- /// <para></para>
- /// Otherwise, it counts the number of Animators in the children of the `gameObject` then does
- /// the same for each parent. If it finds a parent with a different number of child Animators, it
- /// assumes that object is the parent of multiple characters and returns the previous parent as the root.
- /// </remarks>
- ///
- /// <example>
- /// <h2>Simple Hierarchy</h2>
- /// <code>
- /// - Character - Rigidbody, etc.
- /// - Model - Animator, AnimancerComponent
- /// - States - Various components which reference the AnimationClips they will play
- /// </code>
- /// Passing the <c>Model</c> into this method will return the <c>Character</c> because it has the same
- /// number of Animator components in its children.
- ///
- /// <h2>Shared Hierarchy</h2>
- /// <code>
- /// - Characters - Empty object used to group all characters
- /// - Character - Rigidbody, etc.
- /// - Model - Animator, AnimancerComponent
- /// - States - Various components which reference the AnimationClips they will play
- /// - Another Character
- /// - Model
- /// - States
- /// </code>
- /// <list type="bullet">
- /// <item><c>Model</c> has one Animator and no more in its children.</item>
- /// <item>And <c>Character</c> has one Animator in its children (the same one).</item>
- /// <item>But <c>Characters</c> has two Animators in its children (one on each character).</item>
- /// </list>
- /// So it picks the <c>Character</c> as the root.
- ///
- /// <h2>Complex Hierarchy</h2>
- /// <code>
- /// - Character - Rigidbody, etc.
- /// - Model - Animator, AnimancerComponent
- /// - States - Various components which reference the AnimationClips they will play
- /// - Another Model - Animator (maybe the character is holding a gun which has a reload animation)
- /// </code>
- /// In this case, the automatic system would see that the <c>Character</c> already has more child
- /// <see cref="Animator"/>s than the selected <c>Model</c> so it would only return the <c>Model</c> itself.
- /// This can be fixed by making any of the scripts on the <c>Character</c> implement <see cref="ICharacterRoot"/>
- /// to tell the system which object you want it to use as the root.
- /// </example>
- public static Transform FindRoot(GameObject gameObject)
- {
- var root = gameObject.GetComponentInParent<ICharacterRoot>();
- if (root != null)
- return root.transform;
- #if UNITY_EDITOR
- var path = UnityEditor.AssetDatabase.GetAssetPath(gameObject);
- if (!string.IsNullOrEmpty(path))
- return gameObject.transform.root;
- var status = UnityEditor.PrefabUtility.GetPrefabInstanceStatus(gameObject);
- if (status != UnityEditor.PrefabInstanceStatus.NotAPrefab)
- {
- gameObject = UnityEditor.PrefabUtility.GetOutermostPrefabInstanceRoot(gameObject);
- return gameObject.transform;
- }
- #endif
- var animators = ListPool.Acquire<Animator>();
- gameObject.GetComponentsInChildren(true, animators);
- var animatorCount = animators.Count;
- var parent = gameObject.transform;
- while (parent.parent != null)
- {
- animators.Clear();
- parent.parent.GetComponentsInChildren(true, animators);
- if (animatorCount == 0)
- animatorCount = animators.Count;
- else if (animatorCount != animators.Count)
- break;
- parent = parent.parent;
- }
- ListPool.Release(animators);
- return parent;
- }
- /************************************************************************************************************************/
- /// <summary>[Editor-Only]
- /// Calls <see cref="FindRoot(GameObject)"/> if the specified `obj` is a
- /// <see cref="GameObject"/> or <see cref="Component"/>.
- /// </summary>
- public static Transform FindRoot(Object obj)
- {
- if (obj is ICharacterRoot iRoot)
- return iRoot.transform;
- return TryGetGameObject(obj, out var gameObject)
- ? FindRoot(gameObject)
- : null;
- }
- /************************************************************************************************************************/
- /// <summary>[Editor-Only]
- /// Outputs the <see cref="GameObject"/> assignated with the `obj` and returns true if it exists.
- /// </summary>
- /// <remarks>
- /// If the `obj` is a <see cref="GameObject"/> it is used as the result.
- /// <para></para>
- /// Or if the `obj` is a <see cref="Component"/> then its <see cref="Component.gameObject"/>
- /// is used as the result.
- /// </remarks>
- public static bool TryGetGameObject(Object obj, out GameObject gameObject)
- {
- if (obj is GameObject go)
- {
- gameObject = go;
- return true;
- }
- if (obj is Component component)
- {
- gameObject = component.gameObject;
- return true;
- }
- gameObject = null;
- return false;
- }
- /************************************************************************************************************************/
- }
- /************************************************************************************************************************/
- #endif
- /************************************************************************************************************************/
- }
|