reckfellENGINE

Ragdoll: physical bones and partial simulation

A complete three-bone chain: name binding, joints, start/stop and branch selection.

On this page

Run the physical skeleton

Attach RagdollLab3D.cs to an Entity3D root in a separate C# level, build and press F6. Use 60 ticks/s, downward gravity 9.8 and unit branch scale. No external model is required: visible capsules represent three physical bone bodies.

Space starts the whole chain, P keeps root stationary and simulates middle plus descendant tip, and R stops/reset the base pose. Bones fall onto the floor; Layer/Mask settings prevent neighboring bodies from colliding with each other. This is a tutorial chain, not an imported human with muscles or a get-up system.

A three-bone physical chain above the floor

RagdollLab3D.cs
C#
using Reckfell;

public partial class RagdollLab3D : Entity3D
{
    public Skeleton3D Skeleton { get; private set; } = null!;
    public PhysicalBoneSimulator3D Simulator { get; private set; } = null!;
    public PhysicalBone3D[] Bodies { get; private set; } = new PhysicalBone3D[3];
    public override void _Ready()
    {
        Skeleton = new Skeleton3D { Name = "Skeleton", Position = new Vector3(0,5,0) }; AddChild(Skeleton);
        string[] names = { "root", "middle", "tip" };
        for (int i = 0; i < names.Length; i++)
        {
            int bone = Skeleton.AddBone(names[i]);
            Skeleton.SetBoneParent(bone, i - 1);
            Skeleton.SetBoneRest(bone, new Transform3D(Basis.Identity, i == 0 ? Vector3.Zero : Vector3.Down));
        }
        Skeleton.ResetBonePoses(resetBoneSkinScale: false);
        Simulator = new PhysicalBoneSimulator3D { Name = "Simulator" }; Skeleton.AddChild(Simulator);
        for (int i = 0; i < Bodies.Length; i++)
        {
            var body = new PhysicalBone3D { Name = "Physical_" + names[i], Mass = 1, CollisionLayer = 4, CollisionMask = 1,
                BodyOffset = new Transform3D(Basis.Identity, Vector3.Down * .5f), JointType = i == 0 ? PhysicalBone3D.JointTypeEnum.None : PhysicalBone3D.JointTypeEnum.Pin };
            body.Set("bone_name", names[i]);
            body.AddChild(new CollisionShape3D { Shape = new CapsuleShape3D { Radius = .14f, Height = 1 } });
            body.AddChild(new MeshInstance3D { Mesh = new CapsuleMesh { Radius = .14f, Height = 1 }, MaterialOverride =
                new StandardMaterial3D { AlbedoColor = new Color(.25f,.45f,1), ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded } });
            Simulator.AddChild(body); Bodies[i] = body;
        }
        var floor = new StaticBody3D { Position = new Vector3(0,-.25f,0), CollisionLayer = 1, CollisionMask = 0 };
        floor.AddChild(new CollisionShape3D { Shape = new BoxShape3D { Size = new Vector3(12,.5f,10) } });
        floor.AddChild(new MeshInstance3D { Mesh = new BoxMesh { Size = new Vector3(12,.5f,10) }, MaterialOverride =
            new StandardMaterial3D { AlbedoColor = new Color(.55f,.55f,.6f), ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded } }); AddChild(floor);
        var camera = new Camera3D { Position = new Vector3(4,3.5f,5), Current = true }; AddChild(camera); camera.LookAt(ToGlobal(new Vector3(0,2.3f,0)));
    }
    public void StartAll() { Simulator.PhysicalBonesStartSimulation(); }
    public void StartLower() { Simulator.PhysicalBonesStartSimulation(new Reckfell.Collections.Array<StringName> { "middle" }); }
    public void StopAndReset()
    {
        Simulator.PhysicalBonesStopSimulation(); Skeleton.ResetBonePoses(resetBoneSkinScale: false); Skeleton.ResetPhysicsInterpolation();
    }
    public override void _UnhandledKeyInput(InputEvent @event)
    {
        if (@event is not InputEventKey { Pressed: true, Echo: false } key) return;
        if (key.PhysicalKeycode == Key.Space) StartAll();
        if (key.PhysicalKeycode == Key.P) { StopAndReset(); StartLower(); }
        if (key.PhysicalKeycode == Key.R) StopAndReset();
    }
}

Hierarchy and binding

Skeleton3D stores bones/rest poses, PhysicalBoneSimulator3D manages the physics modification, and PhysicalBone3D creates bodies/joints. Here the simulator is under Skeleton3D and physical bones under the simulator. Bone names root/middle/tip must match the actual Skeleton.

This C# version sets the physical bone name through dynamic Set("bone_name", name). PhysicalBone3D has no public BoneName SDK property. After insertion in the proper hierarchy, GetBoneId should return the intended index; -1 means binding failed. Tree entity names and skeleton bone names are distinct.

BodyOffset positions the body relative to its bone; JointOffset defines the connection frame. Moving a visible mesh cannot repair an incorrect rest transform because the physics joint remains elsewhere. Each sample capsule body is offset down by half a segment; Pin connects joint positions without an anatomical rotation limit.

Prepare an imported model

The editor offers Create Physical Skeleton for a selected Skeleton3D. Treat generated bodies as a starting point: remove unnecessary small physical bones, simplify shapes and assign reasonable masses. Check bone ancestry, rest pose, units, inherited scale and body centres.

Choose joint degrees of freedom deliberately: Pin, Hinge, ConeTwist or suitable 6DOF. Limits and numeric parameters depend on the backend; an unsupported-field warning is not an active constraint. Avoid initial penetration, extreme mass ratios and overly detailed colliders.

Layer/Mask control collisions with surroundings and other characters. The simulator offers PhysicalBonesAddCollisionException/RemoveCollisionException with a RID for a particular owner/body. Single-object exclusion and self-collision policy differ. Here CollisionLayer = 4 means layer 3, while CollisionMask = 1 includes layer 1. The bones' mask includes the world on layer 1 and excludes other bones on layer 3. A numeric bitmask and a layer number are different values.

Starting, animation and stopping

PhysicalBonesStartSimulation with no list starts the whole physics branch. A name list selects matching bones and descendants, not entities by tree path. Simulator IsSimulatingPhysics and per-body IsSimulatingPhysics answer different questions; check both for partial startup.

Animation, character control and physics should not independently assign one body's final pose at once. On death, disable ordinary control, start the required branch and coordinate collisions. On recovery, choose base animation, CharacterBody placement and transition policy. StopAndReset here stops physics and resets poses; it does not implement smooth blending or getting up.

Both backends were checked for names/IDs, falling, bounded Pin connections, full/partial startup, stop and restart. The image uses ReckfellPhysics3D. This does not test every joint, imported rig/skin, fast impact, device or complete character recovery. Start with few bones and test the chosen backend.

Reckfell Engine documentationBack to top ↑
QUICK LINKSGetting startedEntity hierarchyC# & .NET 10Scenes and prefabs
Reckfell guides and full API K
reckfellENGINE
RECKFELL ACCOUNT

Sign in

Sign in to your Reckfell account.

One account for the entire Reckfell platform.