brunomikoski / Animation-Sequencer

A visual tool that allows you to create animated sequences of tweens and tweak them on editor time.
MIT License
960 stars 116 forks source link

Implementation of a dynamic nested sequence step #68

Closed crekri closed 1 year ago

crekri commented 1 year ago

I want to propose adding a step for automatically getting all nested sequencers from the parent animation controller, and then playing all those nested sequencers automatically. This way, it would make animating dynamic contents (e.g. unknown amount of buttons and text from a popup) very easy. From my knowledge, no such step currently exists.

I originally want to implement it myself and then submit a pull request, but I am having trouble getting the step working for more than 2 layers of nested animation sequencers. Below is the script for reference:

    [Serializable]
    public sealed class PlayNestedSequenceAnimationStep : AnimationStepBase
    {
        public override string DisplayName => "Play Nested Sequence";

        [SerializeField] private float delayPerSequence = 0f;
        [SerializeField] private FlowType flowTypeForNestedSequences = FlowType.Join;

        [SerializeField] private Transform parent;

        private IEnumerable<AnimationSequencerController> GetChildSequencers(Transform parent)
        {
            //Recursively get all child sequencers that don't have a parent sequencer
            foreach (Transform child in parent)
            {
                AnimationSequencerController childSequencer = child.GetComponent<AnimationSequencerController>();

                childSequencer.SetAutoplayMode(AnimationSequencerController.AutoplayType.Nothing);
#if UNITY_EDITOR
                UnityEditor.EditorUtility.SetDirty(childSequencer);
#endif
                if (childSequencer != null)
                {
                    yield return childSequencer;
                }
                else
                {
                    foreach (AnimationSequencerController childSequencer2 in GetChildSequencers(child))
                    {
                        yield return childSequencer2;
                    }
                }
            }
        }

        public override void AddTweenToSequence(Sequence animationSequence)
        {
            Sequence sequence = DOTween.Sequence();
            sequence.SetDelay(Delay);

            float totalDelay = 0;
            foreach (var nestedSequencer in GetChildSequencers(parent))
            {
                var nestedSequence = nestedSequencer.GenerateSequence();
                if (flowTypeForNestedSequences == FlowType.Join)
                {
                    nestedSequence.SetDelay(totalDelay);
                    totalDelay += delayPerSequence;
                    sequence.Join(nestedSequence);
                }
                else
                {
                    sequence.Append(nestedSequence);
                    sequence.AppendInterval(delayPerSequence);
                }
            }

            if (FlowType == FlowType.Join)
                animationSequence.Join(sequence);
            else
                animationSequence.Append(sequence);
        }

        public override void ResetToInitialState()
        {
            foreach (var nestedSequencer in GetChildSequencers(parent))
            {
                nestedSequencer.ResetToInitialState();
            }
        }

        public override string GetDisplayNameForEditor(int index)
        {
            if (parent == null)
            {
                return $"{index}. [Parent Not Set] Play Nested Sequence";
            }

            var nestedCount = GetChildSequencers(parent).Count();
            return $"{index}. Play {nestedCount} Nested Sequences";
        }
    }

To make this system even more useful, I also propose the changes below

If you think this feature is useful, I could write the code and submit a pull request. (Though I would need some help on solving the above issues)

*PS this asset is amazing, thanks for making it!

crekri commented 1 year ago

Just noticed that the current architecture doesn't allow having two or more animation sequencers on the same game object, and if we try to create two different animation sequencer that controls the same game object and have it run more than once (e.g. button animate in > animate out > animate in), it will only work for the first time but not the second time. Seems like what I suggested would be completely breaking, so maybe I should work on my own fork instead.

Is what I am suggested impossible or is it just that I am using the system incorrectly?

brunomikoski commented 1 year ago

Hey @degchris!

Yeah, I do think allowing more than one AnimationController per GameObject right now would cause a number of issues, it might be doable but would require some more in-depth refactoring.

I feel that having a dynamic step like this could cause multiple other issues that would have to be taken care of to properly support this.

I also think this might fall outside of the AnimationSequencer responsibility scope when dealing with a situation like this, this is how would setup:

My Button Prefab would have the following:

- Root Game Object
    -- Button Content
    -- Animations
        --- Animation In (Sequencer Controller) 
        --- Animation Ou (Sequencer Controller)

So in this way, I have full control of what should be enabled/disabled per prefab. And when spawning multiple copies of the same instance, I normally have a list of spawned objects, it's easy to play all as I need.

Does that make sense to you? But yeah, feel free to create your own fork and change what you see fit!

crekri commented 1 year ago

Thanks for the suggestions!

I think this structure solves my problems, and I could still have a dynamic step through the use of additional tag scripts for labeling the animation. Again, thanks for the amazing work!