Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Question Generating Rig at runtime

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

  1. danUnity

    danUnity

    Joined:
    Apr 28, 2015
    Posts:
    229
    Hi,

    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;
    5.  
    6.     RigBuilder rigbuilder;
    7.  
    8.     private void Start() {
    9.         // Add the rig
    10.         Rig rig = (Rig)transform.AddChild<Rig>();
    11.  
    12.         // Add constraint to rig
    13.         MultiAimConstraint constraint = rig.gameObject.AddComponent<MultiAimConstraint>();
    14.         constraint.data.constrainedObject = ConstrainedObject.transform;
    15.         constraint.data.aimAxis = MultiAimConstraintData.Axis.Y;
    16.         WeightedTransformArray sources = new WeightedTransformArray(0);
    17.         sources.Add(new WeightedTransform(SourceObject.transform, 1f));
    18.         constraint.data.sourceObjects = sources;
    19.  
    20.         // Add rig to rigbuilder
    21.         rigbuilder = GetComponent<RigBuilder>();
    22.         rigbuilder.layers.Clear();
    23.         rigbuilder.layers.Add(new RigBuilder.RigLayer(rig, true));
    24.  
    25.         // build rig graph (Does not seems to be doing anything)
    26.         rigbuilder.Build();
    27.     }
    28. }
    29.  
    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

    danUnity

    Joined:
    Apr 28, 2015
    Posts:
    229
    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;
    3.  
    4. public class TestRigBuilderParent : MonoBehaviour
    5. {
    6.     public GameObject SourceObjectA;
    7.     public GameObject SourceObjectB;
    8.     public GameObject ConstrainedObject;
    9.  
    10.     RigBuilder rigbuilder;
    11.  
    12.     private void Start() {
    13.  
    14.         // create rig gameobject
    15.         GameObject rigGameobject = new GameObject("Rig");
    16.         rigGameobject.transform.SetParent(transform, true);
    17.  
    18.         // Add constraint to rig
    19.         BlendConstraint constraint = rigGameobject.AddComponent<BlendConstraint>();
    20.         constraint.data.constrainedObject = ConstrainedObject.transform;
    21.         constraint.data.sourceObjectA = SourceObjectA.transform;
    22.         constraint.data.sourceObjectB = SourceObjectB.transform;
    23.         constraint.data.blendPosition = true;
    24.  
    25.         // Add the rig
    26.         Rig rig = rigGameobject.AddComponent<Rig>();
    27.  
    28.         // Add rig to rigbuilder
    29.         rigbuilder = gameObject.AddComponent<RigBuilder>();
    30.         rigbuilder.layers.Clear();
    31.         rigbuilder.layers.Add(new RigBuilder.RigLayer(rig, true));
    32.  
    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;
    3.  
    4. public class TestRigBuilderMultiAim : MonoBehaviour
    5. {
    6.     public GameObject SourceObject;
    7.     public GameObject ConstrainedObject;
    8.  
    9.     RigBuilder rigbuilder;
    10.  
    11.     private void Start() {
    12.  
    13.         // create rig gameobject
    14.         GameObject rigGameobject = new GameObject("Rig");
    15.         rigGameobject.transform.SetParent(transform, true);
    16.  
    17.         // Add constraint to rig
    18.         MultiAimConstraint constraint = rigGameobject.AddComponent<MultiAimConstraint>();
    19.         constraint.data.constrainedObject = ConstrainedObject.transform;
    20.         WeightedTransformArray sources = new WeightedTransformArray();
    21.         sources.Add(new WeightedTransform(SourceObject.transform, 1f));
    22.         constraint.data.sourceObjects = sources;
    23.  
    24.         // Add the rig
    25.         Rig rig = rigGameobject.AddComponent<Rig>();
    26.  
    27.         // Add rig to rigbuilder
    28.         rigbuilder = gameObject.AddComponent<RigBuilder>();
    29.         rigbuilder.layers.Clear();
    30.         rigbuilder.layers.Add(new RigBuilder.RigLayer(rig, true));
    31.  
    32.         // build rig graph (Does not seems to be doing anything)
    33.         rigbuilder.Build();
    34.     }
    35. }
     
    Last edited: Jul 20, 2020
    grempire and liudgervr like this.
  3. simonbz

    simonbz

    Unity Technologies

    Joined:
    Sep 28, 2015
    Posts:
    295
    Hi,

    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
    esgnn likes this.
  4. danUnity

    danUnity

    Joined:
    Apr 28, 2015
    Posts:
    229
    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:
    https://we.tl/t-xBeviy5m6f

    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

    Daniel
     
  5. simonbz

    simonbz

    Unity Technologies

    Joined:
    Sep 28, 2015
    Posts:
    295
    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();
    3.  
    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

    danUnity

    Joined:
    Apr 28, 2015
    Posts:
    229
    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.
  7. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    Would you guys (or someone else) be willing to throw together some documentation on creating runtime rigs with this? -- (i.e. a step-by-step process, for example?)
    Sometimes it is not clear the underlying reasons for most of the choices you guys make in your implementations / examples, so this would help plenty of people out to understand the underlying approach a lot better.

    I can imagine plenty of people wanting to know how to create runtime rigs in a more straightforward way, myself included, had I not come across this post by accident. :(
     
    Egad_McDad, NotaNaN and danUnity like this.
  8. JudahMantell

    JudahMantell

    Joined:
    Feb 28, 2017
    Posts:
    476
    I'm looking for the same thing! @danUnity, if you wouldn't mind a quick explanation that goes over the steps, I'm sure we would all really appreciate it!
     
  9. acidyumyum

    acidyumyum

    Joined:
    Sep 27, 2018
    Posts:
    1
    I think I got the process pretty straight. I would like to do a youtube tutorial on that but not sure how soon I will get to it done.
     
  10. sangeethaa1496

    sangeethaa1496

    Joined:
    Sep 28, 2020
    Posts:
    1
    Is there any way to create dynamic IK at runtime
     
  11. ASquareDevs

    ASquareDevs

    Joined:
    Jun 2, 2021
    Posts:
    5
    I cannot tell you how much this helped me out. I wish the documentation was a bit more involved cause I spent hours banging my head against a wall trying to figure out why the constraints weren't working at runtime. It was a simple RigBuilder.Build(); call that saved it all. Thanks!
     
  12. daniel_lochner

    daniel_lochner

    Joined:
    Jun 9, 2016
    Posts:
    175
    Hi there! When I remove a constraint at runtime and rebuild, my console gets flooded with the following warning messages:
    Is there a better practice for removing constraints?
     
  13. daniel_lochner

    daniel_lochner

    Joined:
    Jun 9, 2016
    Posts:
    175
    Ah just fixed it by calling
    AnimatorJobExtensions.UnbindAllStreamHandles(animator);
    before building and rebinding!
     
  14. Lorrak

    Lorrak

    Joined:
    Sep 17, 2019
    Posts:
    52
    jesus christ, and how casual user should know this??
    read the animation manual(animation rigging) and it has zero information about this stuff even the API documentation
    great examples of how to use it in scene with already defined character and rig, etc, but basically its for simple prototyping situation, nothing for dynamically loading the character and its items
    would be nice to have how-to
    reload rig in a proper way
    how to add object in character hands and reload the rig properly.(rebuild rebind animator disable enable doesnt work, at least in 2021.3lts didnt work, some handle error.., gonna try with 2022.3lts)
     
    Last edited: Feb 16, 2024
  15. EvilKris

    EvilKris

    Joined:
    Nov 18, 2014
    Posts:
    29
    I find the practice of configuring coroutines to synchronize with frame updates somewhat unconventional, but when it works it works.

    Code (CSharp):
    1.  public void StartRigWeightChangeAfterAnimation(Rig rig, float newWeight)
    2.     {
    3.         //apparently changing weights during animation transitions is illegal
    4.         //so this function is required
    5.  
    6.         if (!rig)
    7.         {
    8.             Debug.LogError("no rig found!");
    9.             return;
    10.         }
    11.        
    12.         StartCoroutine(ChangeRigWeightsDuringTransition(rig, newWeight));
    13.     }
    14.  
    15.     private IEnumerator ChangeRigWeightsDuringTransition(Rig rig, float newWeight)
    16.     {
    17.         // Check if the animator is in transition
    18.         while (_animator.IsInTransition(0))
    19.         {
    20.             // Wait until the transition is finished
    21.             yield return null;
    22.         }
    23.  
    24.         yield return new WaitForEndOfFrame();
    25.  
    26.         // Set the weights after the transition
    27.         // Example: Setting the weight of a rig layer to newWeight
    28.  
    29.         rig.weight = newWeight;
    30.     }