KybernetikGames / animancer

Documentation for the Animancer Unity Plugin.
63 stars 8 forks source link

Sharing a small utility add-on for Animancer #332

Open daenius opened 5 months ago

daenius commented 5 months ago

I made a simple ScriptableObject for running any custom code alongside playing an ITransition similar to the AttackTransition found in Platform Game Kit. It works the same way as Unity's stock StateMachineBehaviour but for Animancer and I found it to be easier to work with thanks to AnimancerState being a more useful data container than the stock AnimatorStateInfo.

An alternative to subclassing or rolling your own ITransition for times when you might not be able to do so. In my case, I find it useful for easily converting existing AnimatorControllers that came with a bunch of StateMachineBehaviour from a previous project or Asset Store to Animancer.

Not sure where would be a good place to share this with the wider community. I'll post the core ScriptableObject class here first so you guys can review it. Am I allowed to post this on Asset Store or Itch.io at some point?

public abstract class AnimancerStateBehaviour : ScriptableObject
{
    public void Play(AnimancerState state, Object userData)
    {
        // Animator doesn't use this not sure about Timeline
        // Safe freebie for binding at run time for things like Particle Systems or CharacterBrain etc
        state.Playable.GetGraph().GetOutput(0).SetUserData(userData);
        state.Root.RequirePostUpdate(new Updater(state, this));
        OnEnter(state);
    }

    public virtual void OnEnter(AnimancerState state)
    {
    }

    public virtual void ProcessFrame(AnimancerState state)
    {
    }

    public virtual void OnExit(AnimancerState state)
    {
    }

    // Retrieve your particle system or CharacterBrain etc
    protected T UserData<T>(AnimancerState state) where T : Object =>
        (T)state.Playable.GetGraph().GetOutput(0).GetUserData();

    #region Updater

    private class Updater : IUpdatable
    {
        public Updater(AnimancerState state, AnimancerStateBehaviour behaviour)
        {
            _state = state;
            _behaviour = behaviour;
            Key = new();
        }

        private readonly AnimancerStateBehaviour _behaviour;
        private readonly AnimancerState _state;
        public Key Key { get; }

        public void Update()
        {
            if (!_state.IsPlaying)
            {
                _state.Root.CancelPostUpdate(this);
                _behaviour.OnExit(_state);
                return;
            }

            _behaviour.ProcessFrame(_state);
        }
    }

    #endregion
}
KybernetikGames commented 5 months ago

What are you using the UserData for? It seems a bit odd to set it every time you play something. I'd probably implement the get and set as extension methods and have some other script set it on each character on startup.

Am I allowed to post this on Asset Store or Itch.io at some point?

Feel free to do so if you think others will find it useful.

daenius commented 5 months ago

What are you using the UserData for? It seems a bit odd to set it every time you play something. I'd probably implement the get and set as extension methods and have some other script set it on each character on startup.

I had it originally to bind arbitrary Object similar to setting up PlayableOutputs, and the idea was this way you wouldn't need to do a clone/instantiate on the ScriptableObject while still having different GameObjects correctly access live values of something. You are right though because it turned out to be less useful than expected so I ended up getting rid of it shortly after xD As you described, I got more mileage out of having the subclasses take care of themselves with binding whatever they need.

As for sharing this later, how should I declare the assembly dependency for Animancer? Should I be depending on Lite or Pro?

KybernetikGames commented 5 months ago

I don't think the Asset Store has a way to specify optional dependencies so for my Platformer Game Kit I just said it needs Animancer at the start of the description.