/************************************************************************************************************************/

            /// <summary>
            /// Takes a `gameObject` and returns the root <see cref="Transform"/> of the character it is part of.
            /// <para></para>
            /// This method first searches all parents for an <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 <see cref="Animator"/>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 <see cref="Animator"/>s, it
            /// assumes that object is the parent of multiple characters and returns the previous parent as the root.
            /// </summary>
            ///
            /// <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 <see cref="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 <see cref="Animator"/> and no more in its children.</item>
            /// <item>And <c>Character</c> has one <see cref="Animator"/> in its children (the same one).</item>
            /// <item>But <c>Characters</c> has two <see cref="Animator"/>s 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);
                }

#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 <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;
                }

                ObjectPool.Release(animators);

                return(parent);
            }
        /************************************************************************************************************************/

        private static void GatherFromComponents(GameObject gameObject, HashSet <AnimationClip> clips)
        {
            var root = AnimancerEditorUtilities.FindRoot(gameObject);

            var components = ObjectPool.AcquireList <MonoBehaviour>();

            root.GetComponentsInChildren(true, components);
            GatherFromComponents(components, clips);
            ObjectPool.Release(components);
        }
        /************************************************************************************************************************/

        /// <summary>
        /// Uses <see cref="HandleDragAndDrop"/> to deal with drag and drop operations involving
        /// <see cref="AnimationClip"/>s of <see cref="IAnimationClipSource"/>s.
        /// </summary>
        public static void HandleDragAndDropAnimations(Rect dropArea, Action <AnimationClip> onDrop)
        {
            HandleDragAndDrop(dropArea, (clip) => !clip.legacy, onDrop);

            HandleDragAndDrop <IAnimationClipSource>(dropArea, null, (source) =>
            {
                var clips = ObjectPool.AcquireList <AnimationClip>();
                source.GetAnimationClips(clips);
                TryDrop(clips, (clip) => !clip.legacy, onDrop, true);
                ObjectPool.Release(clips);
            });
        }
        /************************************************************************************************************************/

        /// <summary>
        /// Sorts an array according to an array of dependants.
        /// If ItemA depends on ItemB, ItemA will be put later in the returned list.
        /// </summary>
        /// <param name="collection">The collection to sort. If any element depends on something that isn't present, it will be added automatically.</param>
        /// <param name="getDependancies">A delegate that can return the dependancies of any given element.</param>
        /// <param name="comparer">The equality comparer to use. Null will use the default comparer.</param>
        /// <param name="ignoreCycles">If false, an <see cref="ArgumentException"/> will be thrown when a cyclic dependancy is encountered</param>
        public static List <T> TopologicalSort <T>(IEnumerable <T> collection, Func <T, IEnumerable <T> > getDependancies, IEqualityComparer <T> comparer = null, bool ignoreCycles = false)
        {
            var sorted   = ObjectPool.AcquireList <T>();
            var visiting = new Dictionary <T, bool>(comparer);

            foreach (var item in collection)
            {
                Visit(item, getDependancies, sorted, visiting, ignoreCycles);
            }

            return(sorted);
        }
                /************************************************************************************************************************/

                public static bool Append(Dictionary <EditorCurveBinding, bool> bindings,
                                          List <EditorCurveBinding> sortedBindings, ref int index, StringBuilder message)
                {
                    var binding = sortedBindings[index];

                    if (binding.type != typeof(Transform))
                    {
                        return(false);
                    }

                    if (string.IsNullOrEmpty(binding.path))
                    {
                        message.AppendLine().Append('>');
                    }
                    else
                    {
                        message.Append(':');
                    }

                    var otherBindings = ObjectPool.AcquireList <EditorCurveBinding>();
                    var flags         = GetFlags(bindings, sortedBindings, ref index, otherBindings, out var anyExists);

                    message.Append(anyExists ? " [o]" : " [x]");

                    var first = true;

                    AppendProperty(message, ref first, flags, PositionFlags, "position", "xyz");
                    AppendProperty(message, ref first, flags, RotationFlags, "rotation", "wxyz");
                    AppendProperty(message, ref first, flags, ScaleFlags, "scale", "xyz");

                    for (int i = 0; i < otherBindings.Count; i++)
                    {
                        if (anyExists)
                        {
                            message.Append(',');
                        }

                        binding = otherBindings[i];
                        message
                        .Append(" [")
                        .Append(bindings[binding] ? 'o' : 'x')
                        .Append("] ")
                        .Append(binding.propertyName);
                    }

                    ObjectPool.Release(otherBindings);

                    return(true);
                }
Exemple #6
0
        /************************************************************************************************************************/

        /// <summary>Removes any items from the `dictionary` that use destroyed objects as their key.</summary>
        public static void RemoveDestroyedObjects <TKey, TValue>(Dictionary <TKey, TValue> dictionary) where TKey : Object
        {
            var oldObjects = ObjectPool.AcquireList <TKey>();

            foreach (var obj in dictionary.Keys)
            {
                if (obj == null)
                {
                    oldObjects.Add(obj);
                }
            }
            for (int i = 0; i < oldObjects.Count; i++)
            {
                dictionary.Remove(oldObjects[i]);
            }
            ObjectPool.Release(oldObjects);
        }
        /************************************************************************************************************************/

        /// <summary>
        /// Sorts an array according to an array of dependants.
        /// If ItemA depends on ItemB, ItemA will be put later in the returned list.
        /// </summary>
        /// <param name="list">The list to sort. If any element depends on something that isn't present, it will be added automatically.</param>
        /// <param name="skip">The index at which to start sorting. Everything before this index is kept in the same order as the input list.</param>
        /// <param name="getDependancies">A delegate that can return the dependancies of any given element.</param>
        /// <param name="comparer">The equality comparer to use. Null will use the default comparer.</param>
        /// <param name="ignoreCycles">If false, an <see cref="ArgumentException"/> will be thrown when a cyclic dependancy is encountered</param>
        public static List <T> TopologicalSort <T>(List <T> list, int skip, Func <T, IEnumerable <T> > getDependancies, IEqualityComparer <T> comparer = null, bool ignoreCycles = false)
        {
            var sorted   = ObjectPool.AcquireList <T>();
            var visiting = new Dictionary <T, bool>(comparer);

            for (int i = 0; i < skip; i++)
            {
                var item = list[i];
                sorted.Add(item);
                visiting.Add(item, false);
            }

            for (; skip < list.Count; skip++)
            {
                Visit(list[skip], getDependancies, sorted, visiting, ignoreCycles);
            }

            return(sorted);
        }
            private static void AppendBindings(StringBuilder message, Dictionary <EditorCurveBinding, bool> bindings, int existingBindings)
            {
                if (bindings == null)
                {
                    return;
                }

                message.AppendLine()
                .Append(LinePrefix + "This message has been copied to the clipboard" +
                        " (in case it is too long for Unity to display in the Console).");

                message.AppendLine()
                .Append(LinePrefix)
                .Append(bindings.Count - existingBindings)
                .Append(" of ")
                .Append(bindings.Count)
                .Append(" bindings do not exist in the Rig: [x] = Missing, [o] = Exists");

                var sortedBindings = ObjectPool.AcquireList <EditorCurveBinding>();

                sortedBindings.AddRange(bindings.Keys);
                sortedBindings.Sort((a, b) =>
                {
                    var result = a.path.CompareTo(b.path);
                    if (result != 0)
                    {
                        return(result);
                    }

                    if (a.type != b.type)
                    {
                        if (a.type == typeof(Transform))
                        {
                            return(-1);
                        }
                        else if (b.type == typeof(Transform))
                        {
                            return(1);
                        }

                        result = a.type.Name.CompareTo(b.type.Name);
                        if (result != 0)
                        {
                            return(result);
                        }
                    }

                    return(a.propertyName.CompareTo(b.propertyName));
                });

                var previousBinding = default(EditorCurveBinding);
                var pathSplit       = NoStrings;

                for (int iBinding = 0; iBinding < sortedBindings.Count; iBinding++)
                {
                    var binding = sortedBindings[iBinding];
                    if (binding.path != previousBinding.path)
                    {
                        var newPathSplit = binding.path.Split('/');

                        var iSegment = Math.Min(newPathSplit.Length - 1, pathSplit.Length - 1);

                        for (; iSegment >= 0; iSegment--)
                        {
                            if (pathSplit[iSegment] == newPathSplit[iSegment])
                            {
                                break;
                            }
                        }
                        iSegment++;

                        if (!string.IsNullOrEmpty(binding.path))
                        {
                            for (; iSegment < newPathSplit.Length; iSegment++)
                            {
                                message.AppendLine();

                                for (int iIndent = 0; iIndent < iSegment; iIndent++)
                                {
                                    message.Append(Strings.Indent);
                                }

                                message.Append("> ").Append(newPathSplit[iSegment]);
                            }
                        }

                        pathSplit = newPathSplit;
                    }

                    if (TransformBindings.Append(bindings, sortedBindings, ref iBinding, message))
                    {
                        continue;
                    }

                    message.AppendLine();

                    if (binding.path.Length > 0)
                    {
                        for (int iIndent = 0; iIndent < pathSplit.Length; iIndent++)
                        {
                            message.Append(Strings.Indent);
                        }
                    }

                    message
                    .Append('[')
                    .Append(bindings[binding] ? 'o' : 'x')
                    .Append("] ")
                    .Append(binding.type.Name)
                    .Append('.')
                    .Append(binding.propertyName);

                    previousBinding = binding;
                }

                ObjectPool.Release(sortedBindings);
            }