Animation rigging package - animations consistently off (with reproduction repo)

I’ve been working on a game that leverages the animation rigging package and have noticed some undesirable behavior. I’d love to hear the dev team’s stance on this!

Repository with reproduction tests: GitHub - aylanonsense/unity-rigging-test

I have a game object with a circle, a square, and a triangle as its children. All the game object does is play three animations in sequence: one where the circle moves up, one where the square moves up, and one where the triangle moves up. Without using the animation rigging package, it looks like this:

https://drive.google.com/file/d/1w_61vXwV4cvcIpzaNBwWEIghjdhyFyq2/view

ISSUE #1
If we were to recreate these animations using constraints from the animation rigging package, it’d instead look like this:

https://drive.google.com/file/d/1QGSDZUiz2hLo_DiHXy0j78P3CTNie6j0/view

When using the animation rigging package, animations erroneously get stuck in their end positions.

ISSUE #2
Speeding up the square’s animation reveals another issue: it never actually fully reaches its end position:

https://drive.google.com/file/d/1wazgq-hB6aTbE2tlL2ZaUwu2spJKgsF_/view

When using the animation rigging package, animated properties never actually reach their end state. This can be difficult to notice unless the distance moved is large or the duration of the animation is small, but it appears to always be true.

ISSUE #3
Without the animation rigging package, adding a small blend between the animations looks like this:

https://drive.google.com/file/d/1Em6EN4yt3k7P6KwUNRJGkNAdPQdHUwka/view

With the animation rigging package, adding a small blend will result in the shapes trying–but failing–to reach their initial starting positions:

https://drive.google.com/file/d/108DcfVMU9uN2hrvF4T4Kab0gUP65-_Zw/view

CONCLUSION
Though I’ve separated these out into three separate issues, I imagine they’re all symptoms of the same architectural issue. What I imagine is happening is this:

  • Every render cycle, the animation system updates all properties that are touched by active animation clips. It doesn’t bother updating anything else
  • These render cycles can fall anywhere in the animation–there’s no guarantee that a render cycle will ever happen on the very last part of an animation (e.g. if an animation lasts 1.5 seconds, the last update for its animated properties might be at t=1.489)

Because of this, animated properties aren’t guaranteed to reach their end value. And animated properties won’t get reset to their default values when an animation clip ends, since at that point the animation system is no longer paying any attention to them.

I’ve really enjoyed working with the animation rigging package and want to continue leveraging it in my game. But I want to emphasize that what I’m seeing here is pretty undesirable behavior. It really compromises the animation rigging package’s value in my mind and has lead to many headaches.

I can imagine a scenario where all of this might be an unavoidable side effect of the architecture of the animation rigging package. If that’s so, that’s a shame. But if there is a way to, say, have the animation system pay attention to animated properties for an additional render cycle after an animation clip ends… that’d be lovely!

But to close this off, my main questions are:

  • Is the dev team aware of this issue and is it possible to fix?
  • Is anyone aware of any workarounds that guarantee animated properties will reach their end values?

Repository with reproduction tests: GitHub - aylanonsense/unity-rigging-test

Thanks!

3 Likes

Hi,

Thank you for that very thorough report. I’ve taken a look at your project and isolated the problem.

The issue your are describing is a side effect of the [SyncSceneToStream] attribute we use in most of the constraints that are provided in Animation Rigging. The intent of that attribute is to guarantee parameters modified in scripts (scene values) are updated when evaluated in the constraint (stream values) provided they are not overriden by animation. Since we don’t know in which context you intend to use the constraints, we need to enforce this attribute to pad for most use cases.

As for workarounds, I would suggest writing custom constraints to cover the cases where you need this workflow. You can very well do without the [SyncSceneToStream] attribute if you know that your constraints are all driven by animation.

Here is a simple constraint I wrote without [SyncSceneToStream] to copy transform values into another. You could use this constraint to bridge your animated values into your other constraints without them being updated with scene values:

using System;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Animations.Rigging;

[Unity.Burst.BurstCompile]
public struct NoSyncTransformJob : IWeightedAnimationJob
{
    public ReadOnlyTransformHandle source;
    public ReadWriteTransformHandle destination;
   
    public void ProcessAnimation(AnimationStream stream)
    {
        float w = jobWeight.Get(stream);
        if (w > 0f)
        {
            source.GetLocalTRS(stream, out var position, out var rotation, out var scale);
            destination.SetLocalTRS(stream, position, rotation, scale);
        }
        else
        {
            AnimationRuntimeUtils.PassThrough(stream, destination);
        }
    }

    public void ProcessRootMotion(AnimationStream stream)
    {
    }

    public FloatProperty jobWeight { get; set; }
}

public class NoSyncTransformJobBinder : AnimationJobBinder<NoSyncTransformJob, NoSyncTransformData>
{
    public override NoSyncTransformJob Create(Animator animator, ref NoSyncTransformData data, Component component)
    {
        var job = new NoSyncTransformJob();
      
        job.source = ReadOnlyTransformHandle.Bind(animator, data.source);
        job.destination = ReadWriteTransformHandle.Bind(animator, data.destination);

        return job;
    }

    public override void Destroy(NoSyncTransformJob job)
    {
    }
}

[Serializable]
public struct NoSyncTransformData : IAnimationJobData
{
    [SerializeField] public Transform source;
    [SerializeField] public Transform destination;
   
    public bool IsValid()
    {
        return source != null && destination != null;
    }

    public void SetDefaultValues()
    {
        source = null;
        destination = null;
    }
}

public class NoSyncTransform :  RigConstraint<
    NoSyncTransformJob,
    NoSyncTransformData,
    NoSyncTransformJobBinder
>
{
}

Additionally, some modifications to MultiReferentialConstraintJob:

diff --git a/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs b/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs
index 8458b61..4842a13 100644
--- a/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs
+++ b/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs
@@ -44,6 +44,8 @@ namespace UnityEngine.Animations.Rigging
                     sources[i] = src;
                 }
+
+                AnimationRuntimeUtils.PassThrough(stream, sources[driverIdx]);
             }
             else
             {
@@ -109,4 +111,4 @@ namespace UnityEngine.Animations.Rigging
             job.offsetTx.Dispose();
         }
     }
-}
\ No newline at end of file
+}

Notice the call to Passthrough in the job. This is an oversight on our part and meant that the driver transform was not updated properly. We’ll make sure to have this change included in a future release of Animation Rigging.

Hope this helps.

1 Like

Thank you for the response @simonbz ! I think I get the gist of what you’re saying. I’ve updated the reproduction repo with test cases that include your custom constraint.

In the mixed test case (where one shape is animated with your custom constraint and the others are animated directly), I noticed the shapes that were animated directly still experienced the regression of getting stuck at/near their end position. In the game I’m building, it’d be useful to be able to directly animate some properties of some objects (where Unity’s natural forward kinematics makes a lot of intuitive sense) and use constraints to animate others. Is there a workaround to fix this regression of Unity’s natural forward kinematics animation? Without having to create a additional constraints that wouldn’t otherwise be necessary? This behavior only occurs when the animation rigging package is included and used in an animation.

Thanks!


https://drive.google.com/file/d/1hN1AObgJL3CFC3ts8klMqRixT60OHcsG/view

Hi,

Unfortunately, as previously stated, this is a behaviour that is introduced by using the attribute [SyncSceneToStream] to account for scene values in constraints.

I would suggest writing your own constraints that would better suit your requirements. The Animation Rigging package has always been built as an extensible framework and the constraints we provide are not the definitive answer to anyone’s problem.