It is my understanding that if you want to, for example, restore a ragdoll to a standing position, you would want to Sample() the first frame of your standing animation, then lerp all the bones of the ragdoll to those transform positions and rotations of the sample.
But I can’t find an example anywhere of how to actually use Sample(). The docs simply create a Sample(), but they don’t show how to use it afterward. Where does the transform data of the animation sample you create get stored, so you can lerp the bone transforms of the ragdoll to those positions and rotations?
Sample() just applies the transforms at that frame of the animation. If you wanted to lerp the bone transforms you would have to store all of these manually, and then call Sample() and then store the new (sampled) transforms, and then lerp the localRotation,localPosition. Sample() might only take affect after the update loop is finished though, I dunno, so you might have to yield return new waitForEndOfFrame(); before storing the transforms again.
But I wouldn’t actually use sample for your particular situation… I am assuming you swapped in a ragdoll version of your model, and now you want to swap back to the animated one… Instead of using Sample() I would just store the transforms from the ragdoll and then apply these to the animated model, and then use crossfade to get back to the idle position.
That was the first thing I attempted, actually. However, I soon discovered that CrossFade only blends the animation in if the model is going from animation to animation. If you manually set the transform positions/rotations of the rig’s bones to be in a pose (such as the crumpled ragdoll pose) and then CrossFade an animation, it just starts playing; no blending.
Oh yeah I remember that now… It’s funny because I actually ran in to the same problem trying to do sort of the same thing. Since I was dealing with ragdolls I was able to use Sample() to achieve a seamless transition between two models (one using rigidbody physics, the other using the CharacterController.)
So I guess the first suggestion is the way to go. Just remember when you Lerp the bones that you are Lerping their localPositions and localRotations not their world positions and rotations. With most rigs you wouldn’t even need to bother with positions at all, as most of the time bones are simply rotating, not actually moving (locally).
Okay I got this working for the most part… what I’m doing is my Player object (that has my character controller and motor and whatnot) is the parent, and the animated player model a child. I Sample() the first frame of the “get up” animation in Start() and store all the bone localPositions and localRotations in arrays. Then when I do a swap, I instantiate the ragdoll and CopyTransformsRecurse() the animated model, then deactivate the animated model. Then, the the ragdoll settles, I re-activate the animated model, CopyTransformsRecurse() the ragdoll, destroy the ragdoll, then lerp the animated model’s bone’s localPositions and localRotations to those I stored in arrays that I sampled from the “get up” animation. Finally, once the lerping is done, I play the get up animation.
It works great, but the only issue is, if for instance there is an explosion force that makes the ragdoll fly away from where the parent object remains (with the charactercontroller and whatnot), after I reactivate the animated model and CopyTransformsRecurse() the ragdoll, once I start lerping it to the sampled pose local positions and rotations it slides to the center of my charactercontroller (so if the ragdoll is blown far away, as the model is lerping to the positions of the first frame of the get up animation, he’ll slide across the floor until hes at the original position). I can’t seem to figure out a way around this; is it because of the localPositions?
That’s because the first (top) bone in your hierarchy’s local position is relative to a parent transform that is not going to move with the ragdoll (because it wont have a collider attached to it, as it’s just an empty transform)
That means that if an explosion sends your character 20 meters in one direction the localPosition of the rootBone will be (20,0,0) or whatever…
what you will have to do is:
After the ragdoll settles:
Copy the localPositions to the animated object the way you are currently doing it.
Store the absolute position of the rootBone. Vector3 cachedRootPosition = rootBone.position;
move the whole animated object (the one with the CharacterController) to cachedRootPosition - animatedRootLocalPosition.
Then reset the absolute position of the rootBone to the cachedRootPosition. (Which is effectively the same thing as setting it’s localPosition to animatedRootLocalPosition.)
Hopefully that’s not overly confusing. You might also want to do the same thing with the rotation of the root bone. Otherwise the character will spin as they get up to face the direction they were before being hit.
(same as above but with localRotations)
Store the absolute rotation of the rootBone. Quaternion cachedRot = rootBone.rotation;
I think I get it, can’t say for sure though until I have Unity in front of me.
My animated object is not the object with the CharacterController though… it is a child object of my CharacterController object, positioned at its center (similar to how the “graphics” child object of the FPS prefab in standard assets). Does that make a difference?
Also, when you mention rootBone, are you talking about the root bone of the ragdoll replacement, or the animated model?
In the instructions I am referring to the rootBone of the animated model. It is highest bone in the hierarchy of the animated model, sometimes this bone actually transforms geometry, sometimes it is just a “handle” it depends on your rig is set up.
It shouldn’t matter that the CharacterController is not on at the root of the animated model. In my instructions on line 3 just make sure you are setting the world position of the root of the animated model .
Edit: I just realized my explanation of the problem you are having is one of two possibilities… the second one I just realized is actually probably more likely. Thankfully the solution is exactly the same.
I said:
“That’s because the first (top) bone in your hierarchy’s local position is relative to a parent transform that is not going to move with the ragdoll (because it wont have a collider attached to it, as it’s just an empty transform)”
It’s also possible/probable that your root bone in your ragdoll is actually the root transform of the whole ragdoll… this means that it’s localPosition is equivalent to its world position, as it doesn’t have a parent.
Also I should clarify something. Where I said animatedRootLocalPosition I am referring to the localPosition of the animated object’s root bone AFTER it has Lerped… in other words, this is the localPosition of the animated object’s root bone you got from sampling the first frame of your “get up” animation. The same is true for the animatedRootLocalRotation.
Thanks for all the help man, I’ll try and solve it when I get home tonight.
One thing I should mention is the “rig” of the character model (both the ragdoll and the one i’m using for animation) is a child object… and when I save the local positions and rotations of my “get up” animation in start, I’m just sampling the first frame and doing something like:
boneTransforms = playerModel.GetComponentsInChildren.<Transform>();
for(var trans : Transform in boneTransforms){
getUpPosArray.Push(trans.localPosition);
getUpRotArray.Push(trans.localRotation);
}
(I later convert them to built in arrays). Then, when the ragdoll is settled and I CopyTransformsRecurse() the animated model to it and destroy it, to get the positions of the animated model back to the get-up first frame pose I do something like:
(except I lerp them). The fact that I’m storing the local position and rotation of the top-level object of the model (which is not the armature/rig handle) might be what you mentioned?
Yes I think that’s the problem. It may also be that when the ragdoll goes flying it’s top-level object doesn’t move (because there’s no collider attached to directly to it… Though I cant be sure of that because I haven’t used ragdolls yet. Check in the editor window after the character goes flying if the top-level object moves with the model, or if it just stays where it is. )
Thanks a bunch, Antitheory! I got it all figured out last night, thanks to your suggestions and some trial and error… basically it was a matter of using the rig armature in some places where I was using the root transform before, and moving the charactercontroller object to the x and z position of the ragdoll after it settles.