// 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
/************************************************************************************************************************/
}