Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only. On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live. Read our full announcement for more information and let us know if you have any questions.

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

Discussion in 'Animation Rigging' started by aylanonsense, Oct 17, 2020.

  1. aylanonsense


    Sep 22, 2019
    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:

    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:

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

    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:

    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:

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

    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:

    florianhanke, atinymelon and clckwrk like this.
  2. simonbz


    Unity Technologies

    Sep 28, 2015

    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:
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Animations;
    4. using UnityEngine.Animations.Rigging;
    6. [Unity.Burst.BurstCompile]
    7. public struct NoSyncTransformJob : IWeightedAnimationJob
    8. {
    9.     public ReadOnlyTransformHandle source;
    10.     public ReadWriteTransformHandle destination;
    12.     public void ProcessAnimation(AnimationStream stream)
    13.     {
    14.         float w = jobWeight.Get(stream);
    15.         if (w > 0f)
    16.         {
    17.             source.GetLocalTRS(stream, out var position, out var rotation, out var scale);
    18.             destination.SetLocalTRS(stream, position, rotation, scale);
    19.         }
    20.         else
    21.         {
    22.             AnimationRuntimeUtils.PassThrough(stream, destination);
    23.         }
    24.     }
    26.     public void ProcessRootMotion(AnimationStream stream)
    27.     {
    28.     }
    30.     public FloatProperty jobWeight { get; set; }
    31. }
    33. public class NoSyncTransformJobBinder : AnimationJobBinder<NoSyncTransformJob, NoSyncTransformData>
    34. {
    35.     public override NoSyncTransformJob Create(Animator animator, ref NoSyncTransformData data, Component component)
    36.     {
    37.         var job = new NoSyncTransformJob();
    39.         job.source = ReadOnlyTransformHandle.Bind(animator, data.source);
    40.         job.destination = ReadWriteTransformHandle.Bind(animator, data.destination);
    42.         return job;
    43.     }
    45.     public override void Destroy(NoSyncTransformJob job)
    46.     {
    47.     }
    48. }
    50. [Serializable]
    51. public struct NoSyncTransformData : IAnimationJobData
    52. {
    53.     [SerializeField] public Transform source;
    54.     [SerializeField] public Transform destination;
    56.     public bool IsValid()
    57.     {
    58.         return source != null && destination != null;
    59.     }
    61.     public void SetDefaultValues()
    62.     {
    63.         source = null;
    64.         destination = null;
    65.     }
    66. }
    68. public class NoSyncTransform :  RigConstraint<
    69.     NoSyncTransformJob,
    70.     NoSyncTransformData,
    71.     NoSyncTransformJobBinder
    72. >
    73. {
    74. }
    Additionally, some modifications to MultiReferentialConstraintJob:
    Code (CSharp):
    1. diff --git a/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs b/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs
    2. index 8458b61..4842a13 100644
    3. --- a/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs
    4. +++ b/Runtime/AnimationJobs/MultiReferentialConstraintJob.cs
    5. @@ -44,6 +44,8 @@ namespace UnityEngine.Animations.Rigging
    6.                      sources[i] = src;
    7.                  }
    8. +
    9. +                AnimationRuntimeUtils.PassThrough(stream, sources[driverIdx]);
    10.              }
    11.              else
    12.              {
    13. @@ -109,4 +111,4 @@ namespace UnityEngine.Animations.Rigging
    14.              job.offsetTx.Dispose();
    15.          }
    16.      }
    17. -}
    18. \ No newline at end of file
    19. +}
    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.
    florianhanke likes this.
  3. aylanonsense


    Sep 22, 2019
    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.

  4. simonbz


    Unity Technologies

    Sep 28, 2015

    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.