Deadcows / MyBox

MyBox is a set of attributes, tools and extensions for Unity
http://deadcow.ru
MIT License
1.93k stars 243 forks source link

Loopable animations not working with AnimationStateReference play extension #63

Closed TheXRMonk closed 4 years ago

TheXRMonk commented 4 years ago

I'm using this code for previewing animation states out of playmode in the editor;

/// <summary>
/// Script to change animation states in editor to preview states in scene
/// </summary>

#if UNITY_EDITOR
[RequireComponent(typeof(Animator))]
[ExecuteInEditMode]
public class AnimationStateInEditor : MonoBehaviour
{
    [AutoProperty, ReadOnly, SerializeField]
    private Animator anim;

    public AnimationStateReference state;

    private void OnValidate()
    {
        anim = GetComponent<Animator>();
    }

    private void Update()
    {
        if (!Application.isPlaying)
        {
            //forces to play in edit mode
            anim.Update(Time.deltaTime);
            anim.Play(state);

        }

    }
}
#endif

While normal animations work fine, loopable animations only plays the first few frames over and over - not the entire animation. Am I doing something wrong here?

Deadcows commented 4 years ago

Yep, I guess I see the problem. But I think normal animations probably should behave same way, like the looping ones. Play method is not just telling "keep playing", it's starting animation from the beginning. And you calling it in the Update method. In editor Update calls much less frequently comparing to playmode (it might be 1-2fps but it depends) so you starting the animation from the beginning over and over a few times a second.

Instead of simply calling it in Update you need to check if state.StateName was changed, and if it was => Play the new state. It might be enough but I'm not sure how looping animations will behave in edit mode. Also for looping animations you may either access playable graph of the animator and check for "ifPlaying", and Play if it is not or you may somehow get animation clip time length and Play every time elapsed time is => clip length.

I didn't do such things, but it's my first ideas

Also, such method might be helpful in your case if something stucks

[ButtonMethod]
private void PlayAnimation() => anim.Play(state);
Deadcows commented 4 years ago

Oh also AutoProperty should make field read only anyway, you don't need to use ReadOnly attribute. But I'm not sure how well AutoProperty will work in this case, in editor. AutoProperty collects values when scene is saved, and it happened OnPlaymode. This way if you just put this MB on GO and did not saved the scene, anim will be null. In this particular case you may access cached animator through AnimationStateReference.

if (Application.isPlaying) return
if (!state.Assigned) return;

state.Animator.Update(Time.deltaTime);
state.Play();
TheXRMonk commented 4 years ago

Isn't that what's happening already in your extension?

public static class AnimationStateReferenceExtension
    {
        public static void Play(this Animator animator, AnimationStateReference state)
        {
            if (!state.Assigned) return;
            animator.Play(state.StateName);
        }
    }

Using the button method it seems like some of my animations that should loop just runs once and goes into my idle state. Does this method take the animation controller transitions into consideration? Otherwise I have no idea why that's happening.

Deadcows commented 4 years ago

Yep, it simply plays animation on the Animator with all the translations and stuff. To separate particular animation you probably need to parse animator controller to receive AnimationClip asset, construct new playable graph on the fly and inject your clip asset and playable output (animated avatar). It's been a while since I dabbled with playables, but it should be relatively easy to do the thing you want There are some links that may help: Playable example, SimpleAnimation example project and also it seems that legacy Animation component might work

Deadcows commented 4 years ago

Share your results when this tool is done if it's possible 😉 I'd like to see how you're using this animation preview mode in your game🤓