How to Implement Ragdoll Physics with Soft Keying in Jolt Physics
Jolt Physics supports soft-keyframed ragdolls by keeping bodies dynamic while driving them toward animation poses using velocities, allowing characters to react to collisions while following keyframes.
Implementing ragdoll physics with soft keying in Jolt Physics requires balancing animation fidelity with physical reactivity. Unlike pure kinematic rigs that ignore collisions, soft-keyframed ragdolls remain dynamic bodies that external forces can disrupt, making them ideal for interactive characters that need to collide with the environment while maintaining locomotion.
Loading and Configuring Ragdoll Settings
Start by loading a ragdoll definition from an ObjectStream file or generating one programmatically. The RagdollLoader utility in the Jolt Physics samples provides a convenient entry point.
Loading from ObjectStream Files
Use RagdollLoader::sLoad() to deserialize a ragdoll definition from a .tof file. This returns a RagdollSettings object that configures the skeletal hierarchy and joint constraints.
#include <Samples/Utils/RagdollLoader.h>
// Load ragdoll settings with dynamic motion type
Ref<RagdollSettings> ragdollSettings = RagdollLoader::sLoad(
"Human.tof",
EMotionType::Dynamic
);
Source: Samples/Utils/RagdollLoader.h lines 30-32.
Stabilizing the Rig
Before instantiation, preprocess the settings to improve solver stability. Call Stabilize() to adjust masses, CalculateConstraintPriorities() to optimize joint solving order, and DisableParentChildCollisions() to prevent adjacent body parts from colliding.
// Optimize constraint solving
ragdollSettings->Stabilize();
ragdollSettings->CalculateConstraintPriorities();
ragdollSettings->DisableParentChildCollisions();
Limiting Velocities for Stability
Soft-keyframed rigs risk simulation instability when animation data instructs rapid movement—such as a head snapping against a wall. Cap the linear velocity on each body part to prevent the solver from generating excessive impulses.
// Limit max linear velocity to prevent jitter
for (BodyCreationSettings &bcs : ragdollSettings->mParts)
{
bcs.mMaxLinearVelocity = 10.0f;
}
Reference: Samples/Tests/Rig/SoftKeyframedRigTest.cpp lines 51-53.
Creating the Runtime Ragdoll
Instantiate the runtime Ragdoll object from your settings and add it to the physics system. The CreateRagdoll() method takes an ID, a collision group, and the target physics system.
// Create runtime ragdoll instance
Ref<Ragdoll> ragdoll = ragdollSettings->CreateRagdoll(
0,
0,
physicsSystem
);
// Add to simulation and activate
ragdoll->AddToPhysicsSystem(EActivation::Activate);
Reference: Samples/Tests/Rig/SoftKeyframedRigTest.cpp lines 55-57.
Sampling Animation and Driving the Pose
The soft-keyframed technique works by sampling target poses from animation data and computing velocities that push dynamic bodies toward those targets.
Loading Animation Data
Load a skeletal animation file and initialize a SkeletonPose that matches the ragdoll's skeleton structure.
// Load animation (e.g., walk cycle)
Ref<SkeletalAnimation> walkAnim;
AssetStream stream("Human/walk.tof", std::ios::in);
ObjectStreamIn::sReadObject(stream.Get(), walkAnim);
// Initialize pose with ragdoll skeleton
SkeletonPose pose;
pose.SetSkeleton(ragdollSettings->GetSkeleton());
Reference: Samples/Tests/Rig/SoftKeyframedRigTest.cpp lines 58-66.
Applying Kinematic Driving
Each frame, sample the animation at the current time, calculate joint matrices, then call DriveToPoseUsingKinematics() to compute the necessary linear and angular velocities. Counteract gravity unless you want the animation to include free-fall.
void PrePhysicsUpdate(const PreUpdateParams &inParams)
{
// Sample target pose from animation
walkAnim->Sample(currentTime, pose);
pose.CalculateJointMatrices();
// Drive ragdoll toward target pose using velocities
ragdoll->DriveToPoseUsingKinematics(pose, inParams.mDeltaTime);
// Cancel gravity to maintain animation trajectory
ragdoll->AddLinearVelocity(
physicsSystem->GetGravity() * inParams.mDeltaTime
);
currentTime += inParams.mDeltaTime;
}
Reference: Samples/Tests/Rig/SoftKeyframedRigTest.cpp lines 84-92.
Complete Implementation Example
Combine all steps into a complete setup function and update loop:
#include <Jolt/Physics/Ragdoll/Ragdoll.h>
#include <Samples/Utils/RagdollLoader.h>
class SoftKeyframedCharacter
{
Ref<RagdollSettings> mSettings;
Ref<Ragdoll> mRagdoll;
Ref<SkeletalAnimation> mAnimation;
SkeletonPose mPose;
float mTime = 0.0f;
public:
void Initialize(PhysicsSystem *inPhysicsSystem)
{
// 1. Load settings
mSettings = RagdollLoader::sLoad("Human.tof", EMotionType::Dynamic);
// 2. Optimize and stabilize
mSettings->Stabilize();
mSettings->CalculateConstraintPriorities();
mSettings->DisableParentChildCollisions();
// 3. Limit velocities for soft-keyframing
for (BodyCreationSettings &bcs : mSettings->mParts)
bcs.mMaxLinearVelocity = 10.0f;
// 4. Create runtime instance
mRagdoll = mSettings->CreateRagdoll(0, 0, inPhysicsSystem);
mRagdoll->AddToPhysicsSystem(EActivation::Activate);
// 5. Load animation
AssetStream stream("Human/walk.tof", std::ios::in);
ObjectStreamIn::sReadObject(stream.Get(), mAnimation);
mPose.SetSkeleton(mSettings->GetSkeleton());
}
void Update(float inDeltaTime, PhysicsSystem *inPhysicsSystem)
{
// Sample animation
mAnimation->Sample(mTime, mPose);
mPose.CalculateJointMatrices();
// Drive physics bodies toward animation pose
mRagdoll->DriveToPoseUsingKinematics(mPose, inDeltaTime);
// Maintain animation trajectory against gravity
mRagdoll->AddLinearVelocity(inPhysicsSystem->GetGravity() * inDeltaTime);
mTime += inDeltaTime;
}
};
Summary
- Load ragdoll definitions using
RagdollLoader::sLoad()from.toffiles or create them programmatically. - Stabilize settings with
Stabilize()andCalculateConstraintPriorities()before instantiation. - Limit
mMaxLinearVelocityon each body part to prevent instability when animations demand rapid movement. - Create runtime ragdolls via
RagdollSettings::CreateRagdoll()and add to the physics system withAddToPhysicsSystem(). - Drive poses using
DriveToPoseUsingKinematics()while counteracting gravity to maintain animation fidelity. - Reference implementation resides in
Samples/Tests/Rig/SoftKeyframedRigTest.cppaccording to the Jolt Physics source code.
Frequently Asked Questions
What is the difference between soft-keyframed and kinematic ragdolls?
Soft-keyframed ragdolls remain dynamic bodies that calculate velocities to reach target poses, allowing them to react to collisions and external forces. Kinematic ragdolls directly teleport bodies to positions, ignoring the physics simulation entirely. Use soft-keyframing when you need characters to collide realistically with the environment while following animation.
Why must I limit mMaxLinearVelocity in soft-keyframed setups?
Without velocity caps, the physics solver generates excessively large impulses when animations instantaneously move body parts into impossible positions—such as a head intersecting a wall. Limiting mMaxLinearVelocity to values like 10.0f keeps the simulation stable by capping the maximum correction speed, as implemented in SoftKeyframedRigTest.cpp.
How do I prevent gravity from pulling down a soft-keyframed ragdoll?
Call ragdoll->AddLinearVelocity(physicsSystem->GetGravity() * deltaTime) after driving the pose. This applies an opposite velocity that exactly counters gravitational acceleration for that frame, allowing the ragdoll to follow the animation trajectory without falling. Remove this compensation if you want the ragdoll to react to gravity while still being driven by animation.
Can I use procedural animation instead of pre-baked animations?
Yes. The SkeletonPose structure accepts any transform data, whether sampled from SkeletalAnimation or generated procedurally. Calculate your procedural joint matrices, populate the SkeletonPose, and pass it to DriveToPoseUsingKinematics() exactly as you would with loaded animation data. The soft-keyframed system treats all pose sources identically.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →