Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. We are looking for feedback on the experimental Unity Safe Mode which is aiming to help you resolve compilation errors faster during project startup.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Help Wanted Generating Rig at runtime

Discussion in 'Animation Previews' started by danUnity, Jul 20, 2020.

  1. danUnity


    Apr 28, 2015

    I want to generate a Rig at runtime once on the Awake function but it does not work. I'm using unity 2019.3.13f1.

    Code (CSharp):
    1. public class TestRigBuilder : MonoBehaviour
    2. {
    3.     public GameObject SourceObject;
    4.     public GameObject ConstrainedObject;
    6.     RigBuilder rigbuilder;
    8.     private void Start() {
    9.         // Add the rig
    10.         Rig rig = (Rig)transform.AddChild<Rig>();
    12.         // Add constraint to rig
    13.         MultiAimConstraint constraint = rig.gameObject.AddComponent<MultiAimConstraint>();
    14. = ConstrainedObject.transform;
    15. = MultiAimConstraintData.Axis.Y;
    16.         WeightedTransformArray sources = new WeightedTransformArray(0);
    17.         sources.Add(new WeightedTransform(SourceObject.transform, 1f));
    18. = sources;
    20.         // Add rig to rigbuilder
    21.         rigbuilder = GetComponent<RigBuilder>();
    22.         rigbuilder.layers.Clear();
    23.         rigbuilder.layers.Add(new RigBuilder.RigLayer(rig, true));
    25.         // build rig graph (Does not seems to be doing anything)
    26.         rigbuilder.Build();
    27.     }
    28. }
    30. /// <summary>
    31. /// Add a child gameobject with the specified component. Returns the added component.
    32. /// </summary>
    33. /// <param name="transform"></param>
    34. /// <returns></returns>
    35. public static Component AddChild<T>(this Transform transform) where T : Component {
    36.     GameObject gameObject = new GameObject(typeof(T).Name);
    37.     gameObject.transform.SetParent(transform);
    38.     return gameObject.AddComponent<T>();
    39. }
  2. danUnity


    Apr 28, 2015
    Alright, after more testings, it seems like some constraint can be generated at runtime and some can't...

    The MultiAimConstraint can't but the BlendConstraint works fine.

    Currently trying to figure out the difference between the two on how they are handling specifically the sources transform reference because I have a feeling that's the problem.

    Here's the code to my second simplified test with BlendConstraint that works:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Animations.Rigging;
    4. public class TestRigBuilderParent : MonoBehaviour
    5. {
    6.     public GameObject SourceObjectA;
    7.     public GameObject SourceObjectB;
    8.     public GameObject ConstrainedObject;
    10.     RigBuilder rigbuilder;
    12.     private void Start() {
    14.         // create rig gameobject
    15.         GameObject rigGameobject = new GameObject("Rig");
    16.         rigGameobject.transform.SetParent(transform, true);
    18.         // Add constraint to rig
    19.         BlendConstraint constraint = rigGameobject.AddComponent<BlendConstraint>();
    20. = ConstrainedObject.transform;
    21. = SourceObjectA.transform;
    22. = SourceObjectB.transform;
    23. = true;
    25.         // Add the rig
    26.         Rig rig = rigGameobject.AddComponent<Rig>();
    28.         // Add rig to rigbuilder
    29.         rigbuilder = gameObject.AddComponent<RigBuilder>();
    30.         rigbuilder.layers.Clear();
    31.         rigbuilder.layers.Add(new RigBuilder.RigLayer(rig, true));
    33.         // build rig graph (Does not seems to be doing anything)
    34.         rigbuilder.Build();
    35.     }
    36. }
    The same test with MultiAim Constraint which does NOT work:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Animations.Rigging;
    4. public class TestRigBuilderMultiAim : MonoBehaviour
    5. {
    6.     public GameObject SourceObject;
    7.     public GameObject ConstrainedObject;
    9.     RigBuilder rigbuilder;
    11.     private void Start() {
    13.         // create rig gameobject
    14.         GameObject rigGameobject = new GameObject("Rig");
    15.         rigGameobject.transform.SetParent(transform, true);
    17.         // Add constraint to rig
    18.         MultiAimConstraint constraint = rigGameobject.AddComponent<MultiAimConstraint>();
    19. = ConstrainedObject.transform;
    20.         WeightedTransformArray sources = new WeightedTransformArray();
    21.         sources.Add(new WeightedTransform(SourceObject.transform, 1f));
    22. = sources;
    24.         // Add the rig
    25.         Rig rig = rigGameobject.AddComponent<Rig>();
    27.         // Add rig to rigbuilder
    28.         rigbuilder = gameObject.AddComponent<RigBuilder>();
    29.         rigbuilder.layers.Clear();
    30.         rigbuilder.layers.Add(new RigBuilder.RigLayer(rig, true));
    32.         // build rig graph (Does not seems to be doing anything)
    33.         rigbuilder.Build();
    34.     }
    35. }
    Last edited: Jul 20, 2020
  3. simonbz


    Unity Technologies

    Sep 28, 2015

    There is nothing indicating that BlendConstraint should behave differently from MultiAimConstraint. I can only guess that in the case you're trying out here, the Blend constraint transforms are already animated in your animator hierarchy, and thus, rebuilding the constraints does not add additional bindings that the animator should know about.

    And I think that's the main issue you're having, you can very well rebuild the PlayableGraph, but you need to ensure that your bindings are updated in the Animator as well.

    Can you try the following?

    calling `animator.Rebind()` like so:
    Code (CSharp):
    1. rigBuilder.Build();
    2. animator.Rebind();
    Or make sure the animator is disabled before you rebuild your rigBuilder graph:
    Code (CSharp):
    1.     private void Start() {
    2.           animator.enabled = false;
    3.           // do stuff
    4.           rigBuilder.Build();
    5.           animator.enabled = true;
    6.     }
    Also, be aware that rebuilding the constraints and rebinding the animator at runtime is costly. It's better having a disabled constraint in your rig that you enable at runtime, than changing the rig at runtime to add that constraint.
    Last edited: Jul 21, 2020
  4. danUnity


    Apr 28, 2015
    Hi Simon,

    I just tried both techniques and it didn't work. I agree with you that there shouldn't be anything different between the MultiAimConstraint and the BlendConstraint but it seems there is one unless I'm missing something. The way the scene is built is the same for both tests I did.

    You can find the extremely simplified project with a Scene and the two scripts that generate the Rig/Constraint on Start. One script is for the MultiAim and then the other one is for the BlendConstraint. If you opent the scene and hit Play, you'll see the BlendConstraint works but not the MultiAim Constraint.

    Here's the WeTransfer link to the project:

    Thank you yes I'm sure it's not efficient at all but I only want to this operation only once on Start in order for the constraint to work.

    Thank you

  5. simonbz


    Unity Technologies

    Sep 28, 2015
    Ah right, I see what it is.

    Screen Shot 2020-07-21 at 4.07.59 PM.png

    When you add a component through the inspector, it also calls the `Reset` function of the MonoBehaviour which set the default values to whatever defaults the script has set. However this is an editor only thing.

    You can make your runtime code work by changing it like so:
    Code (CSharp):
    1. MultiAimConstraint constraint = rigGameobject.AddComponent<MultiAimConstraint>();
    2. constraint.Reset();
    The BlendConstraint worked well in this case because it has fewer parameters, and thus zero-ed out values did not really matter. Although, you would probably want to also call `Reset` on this constraint.

    Animation Rigging constraints make heavy use of C# generics to streamline the various steps of a constraint implementation, but it also means that we cannot set the default values at instantiation like we could in a simple MonoBehaviour.

    Hope this helps :)
  6. danUnity


    Apr 28, 2015
    Ahh yes! That's what I was looking for! Can't believe I didn't see that settings were all wrong before... I guess I was expecting it to behave like a regular MonoBehaviour.

    Thank you for all your help! I have seen other people asking the same question so I'm sure this thread will really help them.

    Thank you again Simon! Your a life saver!
    simonbz likes this.