animancer icon indicating copy to clipboard operation
animancer copied to clipboard

Calling `AnimancerComponent.Play` more than once causes `AnimancerEvent`s to fire more than once on a looping animation

Open slowhei opened this issue 1 year ago • 5 comments

Environment

  • Animancer Version Number: 7.4.2
  • Animancer Pro or Lite: Pro
  • Unity Version: 2022.3.2f1
  • Platform: Windows 11 Home Version 22H2

Description

When adding Animancer events using ClipTransition.Events.Add() on a looping ClipTransition, the event fires once per loop as expected. However, if that event's callback includes a call to AnimancerComponent.Play() on that same ClipTransition, the Animancer event will fire every frame from then on.

This seems like a bug as the documentation for AnimancerComponent.Play() states, "This method is safe to call repeatedly without checking whether the transition was already playing."

Reproduction

Steps to reproduce the bug:

  1. Create a game object with an animator, an animancer component, and this script:
using UnityEngine;

using Animancer;

public class LAB_AnimationLoop : MonoBehaviour {
    [SerializeField] AnimancerComponent animancer;
    [SerializeField] ClipTransition transition;

    void Awake() {
        transition.Events.Add(0.99999f, () => {
            Debug.Log("Cycle Complete");
            animancer.Play(transition);
        });
    }

    void Start() {
        animancer.Play(transition);
    }
}
  1. Remember to drag the Animancer component and an animation clip into the serialized fields using the Unity editor. The animation must be LOOPING animation. (I have not tried this with a non-looping animation, but perhaps the same thing would happen?)

  2. Press play on the Unity editor and notice how "Cycle Complete" will be logged every frame after one loop of the animation.

  3. Comment the line that goes animancer.Play(transition) and press play again.

  4. Notice that "Cycle Complete" only logs once per loop as expected.

slowhei avatar Jul 12 '23 22:07 slowhei

Thanks for the detailed bug report, that made it really easy to find the issue.

The fix is pretty simple:

  1. Open AnimancerState.EventDispatcher.cs.
  2. Go to the second internal AnimancerEvent.Sequence Events property in that file.
  3. Add this at the end of the setter:
if (_State != null)
    _PreviousTime = _State.NormalizedTime;

KybernetikGames avatar Jul 13 '23 00:07 KybernetikGames

I only found one public AnimancerEvent.Sequence Events in that file. Did you mean the internal AnimancerEvent.Sequence Events? I added your code to the internal property, and that fixed the issue.

Thanks for the rapid response!

slowhei avatar Jul 13 '23 16:07 slowhei

Yeah, I must have only made the second one public since releasing the last version.

KybernetikGames avatar Jul 14 '23 00:07 KybernetikGames

I'll keep this issue open so people can see it until I release the next version with the bug fixed.

KybernetikGames avatar Jul 30 '23 00:07 KybernetikGames