예제 #1
0
        /// <summary>Draws a box describing the internal details of the `playable`.</summary>
        internal void DoInternalDetailsGUI(AnimancerPlayable playable)
        {
            if (Event.current.type == EventType.Layout)
            {
                var text = ObjectPool.AcquireStringBuilder();
                playable.AppendInternalDetails(text, "", " - ");
                _UpdateListLabel = text.ReleaseToString();
            }

            if (_UpdateListLabel == null)
            {
                return;
            }

            if (_InternalDetailsStyle == null)
            {
                _InternalDetailsStyle = new GUIStyle(GUI.skin.box)
                {
                    alignment    = TextAnchor.MiddleLeft,
                    wordWrap     = false,
                    stretchWidth = true,
                }
            }
            ;

            using (ObjectPool.Disposable.AcquireContent(out var content, _UpdateListLabel, null, false))
            {
                var height = _InternalDetailsStyle.CalcHeight(content, 0);
                var area   = GUILayoutUtility.GetRect(0, height, _InternalDetailsStyle);
                GUI.Box(area, content, _InternalDetailsStyle);

                CheckContextMenu(area, playable);
            }
        }
예제 #2
0
        /// <summary>
        /// Ensures that the <see cref="_BoneCount"/> is positive and not larger than the number of bones between the
        /// <see cref="_EndBone"/> and the <see cref="Animator"/>.
        /// </summary>
        /// <remarks>
        /// Called by the Unity Editor in Edit Mode whenever an instance of this script is loaded or a value is changed
        /// in the Inspector.
        /// </remarks>
        private void Validate(AnimancerPlayable animancer, int boneCount, Transform endBone)
        {
            if (boneCount < 1)
            {
                boneCount = 1;
            }
            else if (endBone != null && animancer != null && animancer.Component.Animator != null)
            {
                var root = animancer.Component.Animator.transform;

                var bone = endBone;
                for (int i = 0; i < boneCount; i++)
                {
                    bone = bone.parent;
                    if (bone == root)
                    {
                        boneCount = i + 1;
                        break;
                    }
                    else if (bone == null)
                    {
                        endBone = null;
                        Debug.LogWarning("The End Bone must be a child of the Animator.");
                        break;
                    }
                }
            }
        }
예제 #3
0
        /************************************************************************************************************************/
        #region Context Menu
        /************************************************************************************************************************/

        /// <summary>
        /// Checks if the current event is a context menu click within the `clickArea` and opens a context menu with various
        /// functions for the `playable`.
        /// </summary>
        private void CheckContextMenu(Rect clickArea, AnimancerPlayable playable)
        {
            if (!AnimancerGUI.TryUseClickEvent(clickArea, 1))
            {
                return;
            }

            var menu = new GenericMenu();

            menu.AddDisabledItem(new GUIContent(playable.Graph.GetEditorName() ?? "Unnamed Graph"), false);
            menu.AddDisabledItem(new GUIContent("Command Count: " + playable.CommandCount), false);
            menu.AddDisabledItem(new GUIContent("Frame ID: " + playable.FrameID), false);
            AddDisposablesFunctions(menu, playable.Disposables);

            AddUpdateModeFunctions(menu, playable);
            AnimancerEditorUtilities.AddContextMenuIK(menu, playable);
            AddRootFunctions(menu, playable);

            menu.AddSeparator("");

            AddDisplayOptions(menu);

            menu.AddItem(new GUIContent("Log Details Of Everything"), false,
                         () => Debug.Log(playable.GetDescription(), playable.Component as Object));
            AddPlayableGraphVisualizerFunction(menu, "", playable._Graph);

            AnimancerEditorUtilities.AddDocumentationLink(menu, "Inspector Documentation", Strings.DocsURLs.Inspector);

            menu.ShowAsContext();
        }
            /************************************************************************************************************************/

            private void DoCurrentAnimationGUI(AnimancerPlayable animancer)
            {
                string text;

                if (animancer != null)
                {
                    var transition = Transition;
                    if (transition.IsValid && transition.Key != null)
                    {
                        text = animancer.States.GetOrCreate(transition).ToString();
                    }
                    else
                    {
                        text = transition.ToString();
                    }
                }
                else
                {
                    text = _Instance._TransitionProperty.Property.GetFriendlyPath();
                }

                if (text != null)
                {
                    EditorGUILayout.LabelField("Current Animation", text);
                }
            }
예제 #5
0
        /************************************************************************************************************************/

        private void OnSwingEnd()
        {
            _State = State.Idle;

            // Since the swing animation used for this example has an 'End' event, we can allow it to determine how
            // long the fade should take instead of having this script pick a value (or using the default 0.3 seconds).
            var fadeDuration = AnimancerPlayable.GetFadeOutDuration();

            _Animancer.CrossFade(_Idle, fadeDuration);
        }
예제 #6
0
        /************************************************************************************************************************/

        /// <summary>
        /// As with <see cref="HitBall"/>, this method is called in various different ways depending on which event
        /// system is being used.
        /// <para></para>
        /// Most of them register this method to be called when the <see cref="_Swing"/> animation ends, but
        /// <see cref="GolfHitControllerAnimancer"/> assumes that the event was already set up in the Inspector.
        /// </summary>
        public void EndSwing()
        {
            _State = State.Idle;

            // Since the swing animation is ending early, we want it to calculate the fade duration to fade out over
            // the remainder of that animation instead of the value specified by the _Idle transition.
            var fadeDuration = AnimancerPlayable.GetFadeOutDuration();

            _Animancer.Play(_Idle, fadeDuration);
        }
예제 #7
0
        /************************************************************************************************************************/

        /// <summary>Adds menu functions to set the <see cref="DirectorUpdateMode"/>.</summary>
        private void AddUpdateModeFunctions(GenericMenu menu, AnimancerPlayable playable)
        {
            var modes = Enum.GetValues(typeof(DirectorUpdateMode));

            for (int i = 0; i < modes.Length; i++)
            {
                var mode = (DirectorUpdateMode)modes.GetValue(i);
                menu.AddItem(new GUIContent("Update Mode/" + mode), playable.UpdateMode == mode,
                             () => playable.UpdateMode = mode);
            }
        }
예제 #8
0
        /************************************************************************************************************************/

        /// <summary>Adds functions for controlling the `playable`.</summary>
        public static void AddRootFunctions(GenericMenu menu, AnimancerPlayable playable)
        {
            menu.AddFunction("Add Layer",
                             playable.Layers.Count < AnimancerPlayable.LayerList.DefaultCapacity,
                             () => playable.Layers.Count++);
            menu.AddFunction("Remove Layer",
                             playable.Layers.Count > 0,
                             () => playable.Layers.Count--);

            menu.AddItem(new GUIContent("Keep Children Connected ?"),
                         playable.KeepChildrenConnected,
                         () => playable.KeepChildrenConnected = !playable.KeepChildrenConnected);
        }
예제 #9
0
        /************************************************************************************************************************/
        #region Initialisation
        /************************************************************************************************************************/

        public SimpleLean(AnimancerPlayable animancer, Vector3 axis, NativeArray <TransformStreamHandle> leanBones)
        {
            var animator = animancer.Component.Animator;

            _Job = new Job
            {
                root  = animator.BindStreamTransform(animator.transform),
                bones = leanBones,
                axis  = axis,
                angle = AnimancerUtilities.CreateNativeReference <float>(),
            };

            CreatePlayable(animancer);

            animancer.Disposables.Add(this);
        }
예제 #10
0
        public Damping(AnimancerPlayable animancer, int boneCount, Transform endBone)
        {
            // Create the job and initialise all its arrays.
            // They are all Persistent because we want them to last for the full lifetime of the job.
            // Most of them can use UninitializedMemory which is faster because we will be immediately filling them.
            // But the velocities will use the default ClearMemory to make sure all the values start at zero.

            Validate(animancer, boneCount, endBone);

            // Since we are about to use these values several times, we can shorten the following lines a bit by using constants:
            const Allocator          Persistent          = Allocator.Persistent;
            const NativeArrayOptions UninitializedMemory = NativeArrayOptions.UninitializedMemory;

            _Job = new DampingJob()
            {
                jointHandles   = new NativeArray <TransformStreamHandle>(boneCount, Persistent, UninitializedMemory),
                localPositions = new NativeArray <Vector3>(boneCount, Persistent, UninitializedMemory),
                localRotations = new NativeArray <Quaternion>(boneCount, Persistent, UninitializedMemory),
                positions      = new NativeArray <Vector3>(boneCount, Persistent, UninitializedMemory),
                velocities     = new NativeArray <Vector3>(boneCount, Persistent),
            };

            // Initialise the contents of the arrays for each bone.
            var animator = animancer.Component.Animator;
            var bone     = endBone;

            for (int i = boneCount - 1; i >= 0; i--)
            {
                _Job.jointHandles[i]   = animator.BindStreamTransform(bone);
                _Job.localPositions[i] = bone.localPosition;
                _Job.localRotations[i] = bone.localRotation;
                _Job.positions[i]      = bone.position;

                bone = bone.parent;
            }

            _Job.rootHandle = animator.BindStreamTransform(bone);

            // Add the job to Animancer's output.
            // animancer.InsertOutputJob(_Job);

            CreatePlayable(animancer);

            animancer.Disposables.Add(this);
        }
예제 #11
0
            /************************************************************************************************************************/

            private void DoCurrentAnimationGUI(AnimancerPlayable animancer)
            {
                const string Label = "Current Animation";

                var enabled = GUI.enabled;

                GUI.enabled = false;

                string text = null;

                if (animancer != null)
                {
                    var transition = _Instance.GetTransition();
                    var state      = animancer.States[transition];

                    if (state != null)
                    {
                        var mainObject = state.MainObject;
                        if (mainObject != null)
                        {
                            EditorGUILayout.ObjectField(Label, mainObject, typeof(Object), true);
                        }
                        else
                        {
                            text = state.ToString();
                        }
                    }
                    else
                    {
                        text = _Instance._TransitionProperty.Property.GetFriendlyPath();
                    }
                }
                else
                {
                    text = _Instance._TransitionProperty.Property.GetFriendlyPath();
                }

                if (text != null)
                {
                    EditorGUILayout.LabelField(Label, text);
                }

                GUI.enabled = enabled;
            }
예제 #12
0
        /************************************************************************************************************************/
        #region Gathering
        /************************************************************************************************************************/

        /// <summary>
        /// Initializes an editor in the list for each layer in the `animancer`.
        /// <para></para>
        /// The `count` indicates the number of elements actually being used. Spare elements are kept in the list in
        /// case they need to be used again later.
        /// </summary>
        internal static void GatherLayerEditors(AnimancerPlayable animancer, List <AnimancerLayerDrawer> editors, out int count)
        {
            count = animancer.Layers.Count;
            for (int i = 0; i < count; i++)
            {
                AnimancerLayerDrawer editor;
                if (editors.Count <= i)
                {
                    editor = new AnimancerLayerDrawer();
                    editors.Add(editor);
                }
                else
                {
                    editor = editors[i];
                }

                editor.GatherStates(animancer.Layers._Layers[i]);
            }
        }
예제 #13
0
        /************************************************************************************************************************/

        private void DoRootGUI(AnimancerPlayable playable)
        {
            var labelWidth = EditorGUIUtility.labelWidth;

            AnimancerGUI.BeginVerticalBox(GUI.skin.box);

            using (ObjectPool.Disposable.AcquireContent(out var label, "Is Graph Playing"))
            {
                const string SpeedLabel = "Speed";

                var isPlayingWidth = AnimancerGUI.CalculateLabelWidth(label.text);
                var speedWidth     = AnimancerGUI.CalculateLabelWidth(SpeedLabel);

                var area          = AnimancerGUI.LayoutSingleLineRect();
                var isPlayingArea = area;
                var speedArea     = area;
                isPlayingArea.width = isPlayingWidth + AnimancerGUI.ToggleWidth;
                speedArea.xMin      = isPlayingArea.xMax;

                EditorGUIUtility.labelWidth = isPlayingWidth;
                playable.IsGraphPlaying     = EditorGUI.Toggle(isPlayingArea, label, playable.IsGraphPlaying);

                EditorGUIUtility.labelWidth = speedWidth;
                EditorGUI.BeginChangeCheck();
                var speed = EditorGUI.FloatField(speedArea, SpeedLabel, playable.Speed);
                if (EditorGUI.EndChangeCheck())
                {
                    playable.Speed = speed;
                }
                if (AnimancerGUI.TryUseClickEvent(speedArea, 2))
                {
                    playable.Speed = playable.Speed != 1 ? 1 : 0;
                }
            }

            AnimancerGUI.EndVerticalBox(GUI.skin.box);
            EditorGUIUtility.labelWidth = labelWidth;

            CheckContextMenu(GUILayoutUtility.GetLastRect(), playable);
        }
예제 #14
0
        /************************************************************************************************************************/

        /// <summary>
        /// Draws the <see cref="Animator.updateMode"/> field with any appropriate warnings.
        /// </summary>
        private void DoUpdateModeGUI(bool showWithoutWarning)
        {
            if (_UpdateMode == null)
            {
                return;
            }

            var label = AnimancerGUI.TempContent("Update Mode",
                                                 "Controls when and how often the animations are updated");

            var initialUpdateMode = Targets[0].InitialUpdateMode;
            var updateMode        = (AnimatorUpdateMode)_UpdateMode.intValue;

            EditorGUI.BeginChangeCheck();

            if (!EditorApplication.isPlaying || !AnimancerPlayable.HasChangedToOrFromAnimatePhysics(initialUpdateMode, updateMode))
            {
                if (showWithoutWarning)
                {
                    EditorGUILayout.PropertyField(_UpdateMode, label);
                }
            }
            else
            {
                GUILayout.BeginHorizontal();

                var color = GUI.color;
                GUI.color = AnimancerGUI.WarningFieldColor;
                EditorGUILayout.PropertyField(_UpdateMode, label);
                GUI.color = color;

                label = AnimancerGUI.TempContent("Revert", "Revert to initial mode");
                if (GUILayout.Button(label, EditorStyles.miniButton, AnimancerGUI.DontExpandWidth))
                {
                    _UpdateMode.intValue = (int)initialUpdateMode.Value;
                }

                GUILayout.EndHorizontal();

                EditorGUILayout.HelpBox(
                    "Changing to or from AnimatePhysics mode at runtime has no effect when using the" +
                    " Playables API. It will continue using the original mode it had on startup.",
                    MessageType.Warning);

                if (AnimancerGUI.TryUseClickEventInLastRect())
                {
                    EditorUtility.OpenWithDefaultApp(Strings.DocsURLs.UpdateModes);
                }
            }

            if (EditorGUI.EndChangeCheck())
            {
                _OnEndGUI += () =>
                {
                    for (int i = 0; i < _Animators.Length; i++)
                    {
                        var animator = _Animators[i];
                        if (animator != null)
                        {
                            AnimancerEditorUtilities.Invoke(animator, "OnUpdateModeChanged");
                        }
                    }
                };
            }
        }
        /************************************************************************************************************************/

        private void DoAnimatorGUI(SerializedProperty property, GUIContent label)
        {
            var hasAnimator = property.objectReferenceValue != null;

            var color = GUI.color;

            if (!hasAnimator)
            {
                GUI.color = AnimancerGUI.WarningFieldColor;
            }

            EditorGUILayout.PropertyField(property, label);

            if (!hasAnimator)
            {
                GUI.color = color;

                EditorGUILayout.HelpBox($"An {nameof(Animator)} is required in order to play animations." +
                                        " Click here to search for one nearby.",
                                        MessageType.Warning);

                if (AnimancerGUI.TryUseClickEventInLastRect())
                {
                    Serialization.ForEachTarget(property, (targetProperty) =>
                    {
                        var target = (IAnimancerComponent)targetProperty.serializedObject.targetObject;

                        var animator = AnimancerEditorUtilities.GetComponentInHierarchy <Animator>(target.gameObject);
                        if (animator == null)
                        {
                            Debug.Log($"No {nameof(Animator)} found on '{target.gameObject.name}' or any of its parents or children." +
                                      " You must assign one manually.", target.gameObject);
                            return;
                        }

                        targetProperty.objectReferenceValue = animator;
                    });
                }
            }
            else if (property.objectReferenceValue is Animator animator)
            {
                if (animator.gameObject != Targets[0].gameObject)
                {
                    EditorGUILayout.HelpBox(
                        $"It is recommended that you keep this component on the same {nameof(GameObject)}" +
                        $" as its target {nameof(Animator)} so that they get enabled and disabled at the same time.",
                        MessageType.Info);
                }

                var initialUpdateMode = Targets[0].InitialUpdateMode;
                var updateMode        = animator.updateMode;
                if (AnimancerPlayable.HasChangedToOrFromAnimatePhysics(initialUpdateMode, updateMode))
                {
                    EditorGUILayout.HelpBox(
                        $"Changing to or from {nameof(AnimatorUpdateMode.AnimatePhysics)} mode at runtime has no effect" +
                        $" when using the Playables API. It will continue using the original mode it had on startup.",
                        MessageType.Warning);

                    if (AnimancerGUI.TryUseClickEventInLastRect())
                    {
                        EditorUtility.OpenWithDefaultApp(Strings.DocsURLs.UpdateModes);
                    }
                }
            }
        }
예제 #16
0
        /************************************************************************************************************************/

        /// <summary>Creates a new <see cref="TransitionPreviewDetails"/>.</summary>
        public TransitionPreviewDetails(AnimancerPlayable animancer)
        {
            Animancer = animancer;
        }
예제 #17
0
        /************************************************************************************************************************/

        public TranslateRoot(AnimancerPlayable animancer)
            : this(animancer, Vector3.forward, GetDefaultHumanoidLeanBones(animancer.Component.Animator))
        {
        }
예제 #18
0
        /************************************************************************************************************************/

        public TranslateRoot(AnimancerPlayable animancer, IEnumerable <Transform> leanBones)
            : this(animancer, Vector3.forward, GetTransformStreamHandles(animancer.Component.Animator, leanBones))
        {
        }
예제 #19
0
        /************************************************************************************************************************/

        public SimpleLean(AnimancerPlayable animancer)
            : this(animancer, Vector3.forward, GetDefaultHumanoidLeanBones(animancer.Component.Animator))
        {
        }
예제 #20
0
            /************************************************************************************************************************/

            /// <summary>Creates a new <see cref="DummyAnimancerComponent"/>.</summary>
            public DummyAnimancerComponent(Animator animator, AnimancerPlayable playable)
            {
                Animator          = animator;
                Playable          = playable;
                InitialUpdateMode = animator.updateMode;
            }