KybernetikGames / animancer

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

Animations layer syncing stutters when Animancer inspector is open #293

Closed slowhei closed 1 year ago

slowhei commented 1 year ago

Environment

Description

I have two different Mixer2DTransitionAsset.Unshareds playing concurrently on layers 0 and 1. I sync them by copying the NormalizedTimeD of one AnimancerState to the other (I copy and pasted line 117 from "TimeSynchronizer.cs" in the Animancer source code). I also copy the Parameter value over.

When the Animancer Inspector is not open (i.e. AnimancerComponent is not visible in inspector window), the animation is smooth. However, when the inspector is open, I observe stuttering in the animation. Specifically, the following animation (i.e. the AnimancerState who had a new NormalizedTimeD copied to it) stutters. In the Animancer inspector, the green bar of the following animation stutters as well.

Also, the animation loop's duration is about 1 second.

I'm not sure how interested you are in fixing this bug, but I think that it would be good to include a segment about syncing the normalized time of different Animancer layers in the layers documentation since it's not as simple as follower.NormalizedTimeD = leader.NormalizedTimeD. https://kybernetik.com.au/animancer/docs/manual/blending/layers might be a good place for this.

Reproduction

Steps to reproduce the bug:

  1. Create two Mixer2DTransitionAssets and load them with animations that make big motions quickly (e.g. a running animation, not an idle animation).
  2. Create a game object and add the Animancer component.
  3. Create a Unity component that contains two Mixer2DTransitionAsset.Unshared and assign your transition assets to these variables.
  4. Make these transition assets run on separate layers. The code I used looked something like this:
[SerializeField] Animancer animancer;

[SerializeField] MixerTransition2DAsset.Unshared transition;

[SerializeField] MixerTransition2DAsset.UnShared modifierTransition;
[SerializeField] AvatarMask modifierAvatarMask; // into which I injected an avatar mask that only allows arm movement through
[SerializeField] float modifierWeight = 0.5f;
[SerializeField] int modifierLayer = 1;

void Awake()
{
    animancer.States.GetOrCreate(transition);
    animancer.States.GetOrCreate(modifierTransition);
}

void OnEnable()
{
    animancer.Play(transition);

    AnimancerLayer layer = animancer.Layers[modifierLayer];
    layer.SetMask(modifierAvatarMask);
    layer.Weight = modifierWeight;
    layer.Play(modifierTransition);
}

void Update()
{
    SyncMixers(transition.State, modifierTransition.State);
}

void SyncMixers<T>(MixerState<T> leader, MixerState<T> follower)
{
    if (leader == null || follower == null) return;

    follower.Parameter = leader.Parameter;
    SyncNormalizedTime(leader, follower);

    follower.TimeD = leader.NormalizedTimeD * follower.Length + Time.deltaTime * follower.EffectiveSpeed;
}
  1. When you run the code, open and close the Animancer inspector and notice the stuttering.

  2. If there's no stuttering, try changing the Parameter value a lot while playing Unity. It worsens the stutter.

If you'd like more information, please comment below. Thanks.

KybernetikGames commented 1 year ago

When you say "stuttering" do you mean the animations are actually out of sync or just that the frame rate is low?

Drawing the Inspector has a notable performance cost because there's so much for it to display and it needs to repaint every frame for the time bars to move smoothly which isn't normally needed for other Inspector stuff. You can disable Repaint Constantly in the Display Settings.

slowhei commented 1 year ago

I think it may be an out of sync problem. I played with it a little bit more, and it seems that this only occurs as the parameter value is being changed.

Below is a link to a gif that shows the problem. I am slowly changing the parameter from Vector2.zero to about Vector2(0, 1) and then back to Vector2.zero. As that is happening, the green bar jumps back and forth wildly.

https://drive.google.com/file/d/1o0a9aGMXKcyOPfNbC5B_CDxmItaH06sp/view?usp=sharing

I tried turning off "Repaint Constantly" (really should have tried that before commenting sorry). There is still a bit of stutter, but it's way better. Now I think it's a frame rate issue.

(I would upload a gif to show you what I mean, but the gif frame rate is so bad that you'd think it's a smooth animation.)

slowhei commented 1 year ago

Completely unrelated, but I had a quick question about setting layer weights.

The only way I've successfully set them is by running animancer.Play() followed by animancer.Layers[1].TargetWeight = 0.5f.

I've tried setting Weight, TargetWeight, and calling SetWeight() on the layer object, but none of them work whether I put the code before or after animancer.Layers[1].Play(). Well, to clarify, Weight is indeed successfully set to 0.5f, but Play() always sets target weight to 1, so the weight goes back to 1 anyway.

Is this the intended way to set layer weights?

KybernetikGames commented 1 year ago

Since layers default to 0 weight, Play assumes you want it at 1 so you don't need to also tell it to fade the layer in 99% of the time. If you want it not to you'd need to set your value after calling Play. Most likely you either want to set the Weight or call StartFade.

Also, for future reference, you can drag and drop gifs directly into here instead of needing to upload them separately.

slowhei commented 1 year ago

Ah, Play() assumes a target weight of 1. Didn't know that.

Thank you for the quick responses!