Thursday, October 10, 2013

Unity Mecanim and ragdolls

Update 17 May 2014: If you're interested in physics simulation and character animation, check also our latest research. Also, please note that Mecanim has had some changes since the original post, and now the script could be implemented more elegantly. I also haven't tested the script with different characters - the comments suggest that some characters with different bone structure might not work out of the box.

Sharing a Unity project with a RagdollHelper.cs script that can be used to turn a Mecanim character into a ragdoll and then back. Using the script is simple: there's just one public property called "ragdolled", which can be set to true or false. When the property is changed from true to false, the script blends from the ragdolled pose to a get up animation. This is illustrated in the video below.

Transitioning from Mecanim animation to a ragdoll is as easy as disabling the Animator component of the character and setting all ragdoll rigid bodies to non-kinematic. The reverse is more complex, and my solution is not without kludges, as Mecanim doesn't allow as low level control of the animation as Unity's older animation system. The main problems are that one can't use scripting to enforce an immediate state transition without a blend, and apparently one can't also add states programmatically into an animation controller.

In the old animation system, one could create a new animation clip from the ragdoll's current pose, turn of ragdolling and blend the animation from the created clip to a recovery animation. In Mecanim this is not possible, and after turning Mecanim back on, we have to manually lerp the hip position and slerp all body part rotations back towards the last ragdolled pose in LateUpdate() to achieve a smooth blending. Additionally, we have to wait some frames for Mecanim to transition to the get up animation while using LateUpdate() to move the pose back to the ragdolled one, and then read back the get up animation start body orientation and position, and update Mecanim character's root so that the animated orientation and position match the ragdolled one as well as possible. In the old system we could just update the character transforms with a single script command according to a specific frame of a given animation, and then read whatever info we need. Actually we could possibly use the legacy animation system for that, but then the script would need additional handles to the animation etc., so it seems there's no really simple and beautiful way. One could also perhaps use an editor script to precompute the info.

Here's the Unity project as a zip file in case someone finds it useful: https://docs.google.com/file/d/0B8lAtheEAvgmYUlqX1FjNm84cVU/edit?usp=sharing

Open the test scene, click with mouse on a body part to add an impact to the character, press space to make it get back up.

Update 5 Dec 2013: Today I noticed that Unity 4.3 has added the Animator.Update() which could be used to implement the ragdoll to animation blend more elegantly without the LateUpdate() trickery. I'll have to update this example when I have time.

29 comments:

  1. This is very cool - thank you for posting this.....was looking for something exactly like this!!!!!

    ReplyDelete
  2. Great work!, very useful, thanks for sharing!

    ReplyDelete
  3. I agree, this is great stuff. Thanks for providing it!

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hello. This is awesome!
    I want to ask you, can you give me a reference to this model? I really want to use it since it looks pretty much like this one: http://www.characterink.com/2011/10/24/free-unity-character/
    and I would like to use both of them in a game, but couldn't find the source of the warrior you are using.
    Thanks!

    ReplyDelete
  6. Hello, this is great work! but if i use my own character, the most of the time it goes back to standing position the right way, But sometime it does not play the animation (standUpFromBack - StandUpFromBelly) end goes directly to the idle animation, kinda looks weird. Did you find a solution for this?

    ReplyDelete
  7. Hi there! I seem to have the same problem as Kevin above. Any thoughts? 4/5 times it will work probably, but sometimes it goes right from ragdoll to idle and looks super weird. I've tried a million things and the only difference i can determine is the character model.

    ReplyDelete
    Replies
    1. I was having the same issue. The problem is if you have any atomic transitions in your animator. If when your model was ragdolled it was in mid atomic transition then it wont allow it to change to the getup transition.
      The solution is to set all your transitions to atomic=false;

      Delete
  8. This is just what I needed will help my death animations on stairs and slops look more realistic .. Thank you.

    ReplyDelete
  9. Thank you very much for this tutorial!

    I just have one question: in your setup is it possible to add gravity to the character?
    I have a ragdoll which I only want to use when dying. In the rest of the time I want my character to be affected by gravity while in idle or walking.

    I've managed to achieve this with a RigidBody, but the character slides across collision even when in idle, with speed 0.

    Any help is greatly appreciated!

    ReplyDelete
  10. Thanks for posting the source for this. It was fun to play around with. Here's my results from a little bit of tinkering http://imgur.com/mURZztG

    ReplyDelete
    Replies
    1. Hi, is it possible for you to share how you made the ragdoll be affected by gravity? (I know it's been 2 years now)

      Delete
  11. Thanks, very good work but maybe you give the explanation of the script you made and not the idea of your script, that will be much better. But anyway nice work. You are the guy

    ReplyDelete
  12. Replies
    1. Hi malcolm,
      have you make it worked for unity5??
      mine is not getting up when I press spacebar.
      it says nullPointRefrence

      NullReferenceException: Object reference not set to an instance of an object
      RagdollHelper.set_ragdolled

      Delete
  13. Loved it! And wow! It was in 2013. Man it works great, but i has one problem. When it is blending from ragdoll to mecanim, transforms that are child of a bone behave strange, their positions and rotations change =/
    Can you help me with that? Thanks a lot for the script

    ReplyDelete
    Replies
    1. William, I had the same issue as you: my character holds a sword, and falls to the ground still grasping it. But when he gets up (using any of both animations), the sword is out of his hand. It's still a child of the hand, but it is offset.

      By the way, Perrttu, your script is awesome! Congratulations! And thank you for sharing it!

      I just would really like to know how to solve this small problem.

      Delete
    2. Lucky you, i found a solution just yesterday! Lets talk about the facts: this script recognizes every transform in the ragdoll as a bone, so you don't want your sword to be recognized as a bone. To fix that, you need to ignore all the transforms that are'nt a bone. This is what i did, i went to the Start function and made some changes:

      I put a tag named "Bone" to every bone i wanted to do the transition, and this method checks if the transform has this tag, and then if it does, it adds this transform to the components.

      _components = GetComponentsInChildren(typeof(Transform));
      components = new Component[_components.Length];

      for (int i = 0; i < _components.Length; i++)
      {
      if (_components[i].tag == "Bone")
      {
      components[i] = _components[i];
      }
      }

      Sorry for my bad english, and i hope it helps you! :D

      Delete
    3. Great idea, Wilian!

      I simplified your solution: instead of needing to tag all bones, I just put the condition of having a rigidbody (wich all ragdoll parts have). The Start function ended up like this:

      void Start ()
      {
      //Set all RigidBodies to kinematic so that they can be controlled with Mecanim
      //and there will be no glitches when transitioning to a ragdoll
      setKinematic(true);

      //Find all the transforms in the character, assuming that this script is attached to the root
      Component[] components=GetComponentsInChildren(typeof(Transform));

      //For each of the transforms, create a BodyPart instance and store the transform
      foreach (Component c in components)
      {
      if (c.GetComponent () != null)
      {
      BodyPart bodyPart = new BodyPart ();
      bodyPart.transform = c as Transform;
      bodyParts.Add (bodyPart);
      }
      }

      //Store the Animator component
      anim=GetComponent();
      }

      Delete
    4. Ops! There is the "Rigidbody" part missing after "if (c.GetComponent..."; I believe it is because of the Blogspot API.

      Delete
  14. When I apply script, my character is being kept in spot. Animations work but it just won't move. Why this happens ?

    ReplyDelete
  15. I really like what this does. I have one problem, and I'm hoping you can help me solve it.

    When I add this script to my character, it works, but it makes the character MUCH heavier/harder than it should be. When I hit them with a car, the car is abruptly stopped like hitting a tree, then they topple over slowly.

    My other characters with just a ragdoll flop over the car as they should. Could there be something in here that affects the mass, or force?

    ReplyDelete