// Animancer // Copyright 2020 Kybernetik // using UnityEngine; namespace Animancer { /// /// Interface for components to indicate which is the root of a character when /// is called. /// public interface ICharacterRoot { #pragma warning disable IDE1006 // Naming Styles. /// /// The to search for s beneath. /// /// /// /// Implementing this interface in a will automatically inherit this property so /// you do not need to do anything else: /// public class MyComponent : MonoBehaviour, IAnimancerRoot /// { /// } /// But if you want to have your script point to a different object as the root, you can explicitly implement /// this property: /// public class MyComponent : MonoBehaviour, IAnimancerRoot /// { /// Transform IAnimancerRoot.transform { get { return ???; } } /// } /// Transform transform { get; } #pragma warning restore IDE1006 // Naming Styles. } /************************************************************************************************************************/ #if UNITY_EDITOR /************************************************************************************************************************/ namespace Editor { public static partial class AnimancerEditorUtilities { /************************************************************************************************************************/ /// /// Takes a `gameObject` and returns the root of the character it is part of. /// /// This method first searches all parents for an . If it finds one, it returns the /// . /// /// Otherwise, if the object is part of a prefab then it returns the root of that prefab instance. /// /// Otherwise, it counts the number of s in the children of the `gameObject` then does /// the same for each parent. If it finds a parent with a different number of child s, it /// assumes that object is the parent of multiple characters and returns the previous parent as the root. /// /// /// ///

Simple Hierarchy

/// - Character - Rigidbody, etc. /// - Model - Animator, AnimancerComponent /// - States - Various components which reference the AnimationClips they will play /// Passing the Model into this method will return the Character because it has the same /// number of components in its children. /// ///

Shared Hierarchy

/// - 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 /// /// Model has one and no more in its children. /// And Character has one in its children (the same one). /// But Characters has two s in its children (one on each character). /// /// So it picks the Character as the root. /// ///

Complex Hierarchy

/// - 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) /// In this case, the automatic system would see that the Character already has more child /// s than the selected Model so it would only return the Model itself. /// This can be fixed by making any of the scripts on the Character implement /// to tell the system which object you want it to use as the root. ///
public static Transform FindRoot(GameObject gameObject) { var root = gameObject.GetComponentInParent(); if (root != null) return root.transform; #if UNITY_EDITOR var path = UnityEditor.AssetDatabase.GetAssetPath(gameObject); if (!string.IsNullOrEmpty(path)) return gameObject.transform.root; #if !UNITY_2018_3_OR_NEWER var type = UnityEditor.PrefabUtility.GetPrefabType(gameObject); if (type != UnityEditor.PrefabType.None) { gameObject = UnityEditor.PrefabUtility.FindPrefabRoot(gameObject); return gameObject.transform; } #endif #if UNITY_2018_3_OR_NEWER var status = UnityEditor.PrefabUtility.GetPrefabInstanceStatus(gameObject); if (status != UnityEditor.PrefabInstanceStatus.NotAPrefab) { gameObject = UnityEditor.PrefabUtility.GetOutermostPrefabInstanceRoot(gameObject); return gameObject.transform; } #endif #endif var animators = ObjectPool.AcquireList(); 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; } ObjectPool.Release(animators); return parent; } /************************************************************************************************************************/ /// /// Calls if the specified `obj` is a or /// . /// public static Transform FindRoot(Object obj) { var iRoot = obj as ICharacterRoot; if (iRoot != null) return iRoot.transform; var gameObject = obj as GameObject; if (gameObject == null) { var component = obj as Component; if (component != null) gameObject = component.gameObject; else return null; } return FindRoot(gameObject); } /************************************************************************************************************************/ } } /************************************************************************************************************************/ #endif /************************************************************************************************************************/ }