14.Rigidbodyと物理世界

1404.ラグドール

 このサンプルはFullSample404というディレクトリに含まれます。
 BaseCrossDx11.slnというソリューションを開くとDx11版が起動します。

 実行結果は以下のような画面が出ます。

 

図1404a

 

 Yボタンをいったん離して、もう一度Yボタンを押すと、選択される部位が変わります。これを連続して行うと、続けて選択される部位が変わります。

 

図1404b

 


マルチ物理オブジェクト

 ラグドールはCharacter.h/cppに記述があります。
 この項ではマルチ物理オブジェクトというコンポーネントを使います。RigidbodyMultiというコンポーネントです。
 複数の物理オブジェクトを管理できるコンポーネントです。RigidbodyCombinedと似ているようですが違います。RigidbodyCombined物理オブジェクトを組み合わせて新しい形を作ります。RigidbodyMultiで管理する物理オブジェクトは、あくまで一つ一つは別のものです。
 Character.cppRagdoll::OnCreate()関数を見てください。抜粋ですが以下のような記述があります。
void Ragdoll::OnCreate() {
    //初期位置などの設定
    auto Ptr = GetComponent<Transform>();
    float Scale(1.0f);
    Ptr->SetScale(Vec3(Scale));
    Ptr->SetQuaternion(m_StartQt);
    Ptr->SetPosition(m_StartPos);

    //マルチ物理オブジェクト
    auto PsPtr = AddComponent<RigidbodyMulti>();
    PsPtr->SetDrawActive(true);
    PsPtr->SetAutoTransform(false);

    uint32_t contactFilterSelfA = 0x01;
    uint32_t contactFilterSelfB = 0x02;
    uint32_t contactFilterSelfC = 0x04;
    uint32_t contactFilterSelfD = 0x08;
    uint32_t contactFilterTargetA = 0x0d;
    uint32_t contactFilterTargetB = 0x0a;
    uint32_t contactFilterTargetC = 0x05;
    uint32_t contactFilterTargetD = 0x0b;

    size_t HeadNum, TorsoNum, BodyNum,
        UpperLegLNum, LowerLegLNum, UpperArmLNum, LowerArmLNum,
        UpperLegRNum, LowerLegRNum, UpperArmRNum, LowerArmRNum;


    float inertiaScale = 3.0f;
    {
        //頭部の作成
        PsSphereParam param;
        param.m_Radius = 0.3f;
        param.m_Mass = 3.0f;
        //慣性テンソルの計算
        param.m_Inertia = inertiaScale * BasePhysics::CalcInertiaSphere(param.m_Radius, param.m_Mass);
        param.m_MotionType = PsMotionType::MotionTypeActive;
        param.m_Quat = m_StartQt;
        param.m_Pos = m_StartPos + rotate(m_StartQt, Vec3(0.0f, 3.38433f, 0.0f));
        param.m_ContactFilterSelf = contactFilterSelfB;
        param.m_ContactFilterTarget = contactFilterTargetB;
        //頭部の追加
        HeadNum = PsPtr->AddSphere(param);


    }
    {
        //腰の作成
        PsSphereParam param;
        param.m_Radius = 0.35f;
        param.m_Mass = 10.0f;
        //慣性テンソルの計算
        param.m_Inertia = inertiaScale * BasePhysics::CalcInertiaSphere(param.m_Radius, param.m_Mass);
        param.m_MotionType = PsMotionType::MotionTypeActive;
        param.m_Quat = m_StartQt;
        param.m_Pos = m_StartPos + rotate(m_StartQt, Vec3(0.0f, 1.96820f, 0.0f));
        param.m_ContactFilterSelf = contactFilterSelfB;
        param.m_ContactFilterTarget = contactFilterTargetB;
        //腰の追加
        TorsoNum = PsPtr->AddSphere(param);

    }
    {
        //胴体の作成
        PsCapsuleParam param;
        param.m_HalfLen = 0.1f;
        param.m_Radius = 0.38f;
        param.m_OffsetOrientation = Quat(0.0f, 0.0f, 0.70711f, 0.70711f);
        param.m_Mass = 8.0f;
        //慣性テンソルの計算
        //調整する
        param.m_Inertia = inertiaScale * BasePhysics::CalcInertiaCylinderY(param.m_HalfLen + param.m_Radius, param.m_Radius, param.m_Mass);
        param.m_MotionType = PsMotionType::MotionTypeActive;
        param.m_Quat = m_StartQt;
        param.m_Pos = m_StartPos + rotate(m_StartQt, Vec3(0.0f, 2.66926f, 0.0f));
        param.m_ContactFilterSelf = contactFilterSelfA;
        param.m_ContactFilterTarget = contactFilterTargetA;
        //胴体の追加
        BodyNum = PsPtr->AddCapsule(param);
    }
//中略
    {
        //腰と胴体のジョイント
        PsSwingTwistJointParam jparam;
        jparam.m_AnchorPoint = m_StartPos + rotate(m_StartQt, Vec3(0.0f, 2.26425f, 0.0f));
        jparam.m_TwistAxis = rotate(m_StartQt, Vec3(0.0f, 1.0f, 0.0f));
        jparam.m_IndexA = PsPtr->GetPsObject(TorsoNum)->GetIndex();
        jparam.m_IndexB = PsPtr->GetPsObject(BodyNum)->GetIndex();
        PsPtr->AddSwingTwistJoint(jparam);
    }
    {
        //胴体と頭部のジョイント
        PsSwingTwistJointParam jparam;
        jparam.m_AnchorPoint = m_StartPos + rotate(m_StartQt, Vec3(0.0f, 3.13575f, 0.0f));
        jparam.m_TwistAxis = rotate(m_StartQt, Vec3(0.0f, 1.0f, 0.0f));
        jparam.m_IndexA = PsPtr->GetPsObject(BodyNum)->GetIndex();
        jparam.m_IndexB = PsPtr->GetPsObject(HeadNum)->GetIndex();
        PsPtr->AddSwingTwistJoint(jparam);
    }
//中略
    //ジョイントのパラメータ調整
    for (auto& j : PsPtr->GetPsJointVec()) {
        j->getPfxJoint().m_constraints[0].m_warmStarting = 1;
        j->getPfxJoint().m_constraints[1].m_warmStarting = 1;
        j->getPfxJoint().m_constraints[2].m_warmStarting = 1;

        j->getPfxJoint().m_constraints[3].m_damping = 0.05f;
        j->getPfxJoint().m_constraints[4].m_damping = 0.05f;
        j->getPfxJoint().m_constraints[5].m_damping = 0.05f;
    }
}
 赤くなっているところがRigidbodyMultiコンポーネントにかかわるところです。物理オブジェクトとジョイントを追加しています。
 ここで紹介したラグドールは人型をしています。11個の部位とそれらを結び付けているジョイントで構成されています。
 それらをまとめて管理できるのがRigidbodyMultiコンポーネントです。そういうことなので、前項で紹介したジョイントのサンプルもこのRigidbodyMultiコンポーネントを使って記述できます(そのほうが楽かもしれません)。
 ただ前項はジョイントを強調したかったのでマネージャによる管理方式をとってます。

 ソースを見るとわかりますが、何もラグドールだからといって特別なことをしているわけではなく、一つ一つの部位を作成してはRigidbodyMultiコンポーネントに追加しています。ですので部位のパラメータやジョイントのパラメータを変えれば、違う動きのラグドールを作成できます。人型ではなく動物型昆虫型のも挑戦してみるとよいでしょう。

描画マネージャ

 さてこのサンプルでは描画マネージャとしてラグドールの描画は別オブジェクトにしています。こうすることで、同じラグドールを使っても、別々の描画がしやすいつくりになってます。
 RagdollManagerというクラスです。このクラスでは、ラグドールから一つ一つの部位を取り出し、その位置や姿勢に合わせて球体やカプセルを描画しています。
 テクスチャは実装してませんが、簡単に実装可能です。
 またボーンが入っているモデルで各物理オブジェクトの位置や姿勢モデルのボーンにあてはめることも可能です。この場合グラフィッカーさんとの綿密な計画が必要で、結構根気のいる実装が必要ですが、可能は可能です。