reckfellENGINE

Ragdoll: физические кости и частичная симуляция

Законченная цепочка из трёх костей: привязка по имени, суставы, запуск, остановка и выбор ветви.

На этой странице

Запустить физический скелет

Добавьте RagdollLab3D.cs к корню Entity3D отдельного C#-уровня, соберите и запустите F6. Физика — 60 шагов/с, гравитация 9.8 вниз, единичный масштаб ветви. Внешняя модель не нужна: видимые капсулы показывают физические тела трёх костей.

Space включает всю цепочку, P оставляет root неподвижной и симулирует middle с потомком tip, R останавливает симуляцию и возвращает базовую позу. Кости падают на пол; соседние тела не сталкиваются друг с другом по настройкам Layer/Mask. Это учебная цепочка, а не импортированный человек с мышцами или системой вставания.

Физическая цепочка из трёх костей над полом

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();
    }
}

Иерархия и привязка

Skeleton3D хранит кости/rest-позы, PhysicalBoneSimulator3D управляет физической модификацией, PhysicalBone3D создаёт тела и соединения. В примере simulator находится под Skeleton3D, физические кости — под simulator. Имена костей root/middle/tip должны совпадать с реальным Skeleton.

В C# этой версии имя физической кости задаётся динамическим свойством Set("bone_name", name). Публичного свойства PhysicalBone3D.BoneName в SDK нет. После добавления в правильную иерархию GetBoneId должен вернуть ожидаемый индекс; -1 означает, что привязка не установилась. Не путайте имя сущности в дереве и имя кости скелета.

BodyOffset описывает смещение тела относительно кости, JointOffset — систему соединения. Не компенсируйте ошибочный rest-transform случайным переносом видимого меша: физический сустав останется в другом месте. В примере тело капсулы смещено вниз на половину длины сегмента; Pin связывает положения суставов, но не задаёт анатомический предел вращения.

Подготовить импортированную модель

В редакторе для выбранного Skeleton3D доступно Create Physical Skeleton. Это исходная генерация: затем удалите ненужные мелкие физические кости, упростите формы и задайте правдоподобные массы. Проверьте родство костей, rest-позу, единицы, наследуемый масштаб и положение центров тел.

Настройте суставы под нужные степени свободы: Pin, Hinge, ConeTwist или подходящий 6DOF. Ограничения и числовые параметры зависят от backend; предупреждение о неподдерживаемом поле нельзя считать действующим ограничением. Избегайте начального проникновения, крайних соотношений масс и чрезмерной детализации коллайдеров.

Layer/Mask задают столкновения со средой и другими персонажами. Для исключения конкретного владельца/тела simulator имеет PhysicalBonesAddCollisionException/RemoveCollisionException с RID. Исключение одного объекта и запрет самостолкновений — разные политики. В примере CollisionLayer = 4 означает слой 3, CollisionMask = 1 включает слой 1. Маска костей включает мир на слое 1 и исключает другие кости на слое 3. Числовая битовая маска и номер слоя — разные величины.

Запуск, анимация и остановка

PhysicalBonesStartSimulation без списка запускает всю физическую ветвь. Список имён выбирает соответствующие кости и потомков; это не список сущностей по путям. IsSimulatingPhysics у simulator и у отдельных тел отвечает на разные вопросы, проверяйте оба уровня при частичном запуске.

Анимация, ввод персонажа и физика не должны одновременно независимо назначать окончательную позу одному телу. При смерти отключите обычное управление, включите нужную физическую ветвь и согласуйте коллизии. При возвращении выберите базовую анимацию, место основного CharacterBody и способ перехода. StopAndReset здесь останавливает физику и сбрасывает позы; плавное вставание или смешивание не реализовано.

На обоих backend проверены имена/ID, падение, ограниченная Pin-цепочка, полный/частичный старт, остановка и повторный запуск. Изображение использует ReckfellPhysics3D. Не проверялись все суставы, импортированные rig/skin, высокоскоростные удары, устройства или готовое восстановление персонажа. Начинайте с малого числа костей и проверяйте на выбранном backend.

Документация Reckfell EngineНаверх ↑
БЫСТРЫЙ ПЕРЕХОДНачало работыEntity и иерархияC# & .NET 10Сцены и префабы
Руководства и полный API Reckfell K
reckfellENGINE
RECKFELL ACCOUNT

Вход в аккаунт

Войдите в аккаунт Reckfell.

Один аккаунт для всей платформы Reckfell.