11.チュートリアル

1101.コリジョンを使った3Dの配置

 このサンプルはFullSample101というディレクトリに含まれます。
 BaseCrossDx11VS2017.sln、BaseCrossDx11VS2019.slnというソリューションを開くとDx11版が起動します。
 BaseCrossDx12VS2017.sln、BaseCrossDx12VS2017.slnというソリューションを開くとDx12版が起動します。
 VS2017、VS2019はVisualStdioのバージョンです。

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

 

図1101a

 

 このサンプルからXBoxコントローラ対尾応になります。また、多くのサンプルがキーボードでも操作可能です。

コリジョンとは

 コリジョン(Collision)とは衝突判定のことです。BaseCrossでは球体、カプセル、OBB(直方体)矩形、三角形の集合の形状のコリジョンを持ってました。
 またRigidbodyコンポーネントGravity行動を結びつけることにより、簡易的な物理処理を行っていました。
 BaseCrossではこのほかにPsBodyコンポーネントという完全な物理処理を行うコンポーネントを持っていました。
 BaseCrossではいささか複雑になっていた各オブジェクトの役割をBaseCross64では仕様をスッキリさせました。
 具体的には まず以下の表を見てください。

クラス名 BaseCrossでの実装 BaseCross64での実装
Collision 衝突判定。Rigidbodyと相互関係がある 衝突判定。判定と衝突した相手からのエスケープのみ行う
Gravity 重力。Behavior(行動)として実装。Rigidbodyと相互関係がある 重力。コンポーネントとして実装。Collisionと相互関係がある。
Rigidbody 簡易物理演算。コンポーネントとして実装。CollisionおよびGravityと相互関係がある 物理エンジンとして実装。コンポーネントとして実装
PsBody 物理エンジンとして実装。コンポーネントとして実装 廃止

 このように、CollisionとGravityを使って簡易的な衝突判定とエスケープ、そして重力を実装するか、Rigidbodyを使って、完全な物理処理を実装するか、の2択になります。
 またRigidbodyを使って、完全な物理処理を実装したうえで、Collisionにより、衝突情報を取得するような実装にするか(これについては、チュートリアルではなく、のちのサンプルで紹介します)も可能です。

コリジョンによる3D空間の実装

 このサンプルに配置されているオブジェクトは以下の通りです。

プレイヤー

 プレイヤーはplayer.h/cppに実装されています。
 プレイヤーはコントローラで動かすことができます。Aボタンでジャンプします。
 チュートリアルですので、要点だけ説明します。以下は、プレイヤーのOnCreate()関数です。プレイヤーオブジェクトが構築されるタイミングで呼ばれます。
//初期化
void Player::OnCreate() {

    //初期位置などの設定
    auto Ptr = AddComponent<Transform>();
    Ptr->SetScale(0.25f, 0.25f, 0.25f); //直径25センチの球体
    Ptr->SetRotation(0.0f, 0.0f, 0.0f);
    Ptr->SetPosition(0, 0.125f, 0);

    //CollisionSphere衝突判定を付ける
    auto PtrColl = AddComponent<CollisionSphere>();
    //重力をつける
    auto PtrGra = AddComponent<Gravity>();


    //文字列をつける
    auto PtrString = AddComponent<StringSprite>();
    PtrString->SetText(L"");
    PtrString->SetTextRect(Rect2D<float>(16.0f, 16.0f, 640.0f, 480.0f));

    //影をつける(シャドウマップを描画する)
    auto ShadowPtr = AddComponent<Shadowmap>();
    //影の形(メッシュ)を設定
    ShadowPtr->SetMeshResource(L"DEFAULT_SPHERE");

    //描画コンポーネントの設定
    auto PtrDraw = AddComponent<BcPNTStaticDraw>();
    //描画するメッシュを設定
    PtrDraw->SetMeshResource(L"DEFAULT_SPHERE");
    PtrDraw->SetFogEnabled(true);
    //描画するテクスチャを設定
    PtrDraw->SetTextureResource(L"TRACE_TX");
    SetAlphaActive(true);

    //カメラを得る
    auto PtrCamera = dynamic_pointer_cast<MyCamera>(OnGetDrawCamera());
    if (PtrCamera) {
        //MyCameraである
        //MyCameraに注目するオブジェクト(プレイヤー)の設定
        PtrCamera->SetTargetObject(GetThis<GameObject>());
        PtrCamera->SetTargetToAt(Vec3(0, 0.25f, 0));
    }
}
 赤くなっているところがこの項のポイントです。
 プレイヤーは、コントローラの操作によって移動したりジャンプしてるわけですが、CollisionSphereコンポーネント(球体のコリジョン)を持ち、重力に影響します。
 この記述で、ほかのコリジョンを持っているオブジェクトと衝突判定を行い、必要であれば、お互いの領域からエスケープ(相手の領域から脱出する)実装をします。
 コンポーネントの実装方法や仕組みについては、のちのサンプルで説明しますので、今の段階ではこう覚えておいた方がいいでしょう。

追いかけるオブジェクト

 プレイヤーに移動に合わせ、常に追いかけてくるオブジェクトがあります。この実装はCharacter.h/cppにあります。
 
void SeekObject::OnCreate() {
    auto PtrTransform = GetComponent<Transform>();
    PtrTransform->SetPosition(m_StartPos);
    PtrTransform->SetScale(0.125f, 0.25f, 0.25f);
    PtrTransform->SetRotation(0.0f, 0.0f, 0.0f);

    //オブジェクトのグループを得る
    auto Group = GetStage()->GetSharedObjectGroup(L"SeekGroup");
    //グループに自分自身を追加
    Group->IntoGroup(GetThis<SeekObject>());
    //Obbの衝突判定をつける
    auto PtrColl = AddComponent<CollisionObb>();
    //重力をつける
    auto PtrGra = AddComponent<Gravity>();
    //分離行動をつける
    auto PtrSep = GetBehavior<SeparationSteering>();
    PtrSep->SetGameObjectGroup(Group);
    //影をつける
    auto ShadowPtr = AddComponent<Shadowmap>();
    ShadowPtr->SetMeshResource(L"DEFAULT_CUBE");

    auto PtrDraw = AddComponent<BcPNTStaticDraw>();
    PtrDraw->SetFogEnabled(true);
    PtrDraw->SetMeshResource(L"DEFAULT_CUBE");
    PtrDraw->SetTextureResource(L"TRACE_TX");
    //透明処理をする
    SetAlphaActive(true);

    //ステートマシンの構築
    m_StateMachine.reset(new StateMachine<SeekObject>(GetThis<SeekObject>()));
    //最初のステートをSeekFarStateに設定
    m_StateMachine->ChangeState(FarState::Instance());
}
 プレイヤー同様赤くなっている部分がポイントです。CollisionObbコンポーネントをもち、重力Gravityコンポーネントを実装します。
 このオブジェクトはプレイヤーを追いかけるという機能を実装するためにSeparationSteering(分離)、SeekSteering(追跡)、ArriveSteering(到着)行動が実装されていますが、それらの説明は、のちのサンプルで行います。

動かすことができる箱

 このサンプルの右手斜め前方向に壁模様の箱があります。これはCharacter.h/cppにあるMoveBoxクラスです。以下はその構築用のソースです。
void MoveBox::OnCreate() {
    auto PtrTransform = GetComponent<Transform>();
    PtrTransform->SetScale(m_Scale);
    PtrTransform->SetRotation(m_Rotation);
    PtrTransform->SetPosition(m_Position);
    //OBB衝突j判定を付ける
    auto PtrColl = AddComponent<CollisionObb>();
    //重力をつける
    auto PtrGra = AddComponent<Gravity>();
    //影をつける
    auto ShadowPtr = AddComponent<Shadowmap>();
    ShadowPtr->SetMeshResource(L"DEFAULT_CUBE");
    //描画処理
    auto PtrDraw = AddComponent<BcPNTStaticDraw>();
    PtrDraw->SetFogEnabled(true);
    PtrDraw->SetMeshResource(L"DEFAULT_CUBE");
    PtrDraw->SetTextureResource(L"WALL_TX");
    PtrDraw->SetOwnShadowActive(true);
}
 このオブジェクトもSeekObjectと同じように、CollisionObb衝突判定Gravityのコンポーネントを実装してあります。
 このオブジェクトは自分で動くことはないですが、プレイヤーSeekObjectにぶつかる(小突かれる)ことによって場所を変えます。この動きは、もっぱら当たった相手から離れる(エスケープする)ことによって動きます。

動かない箱

 このステージには動かない箱がいくつかあります。代表的なのは、ステージの床です。これがないと、プレイヤーやSekObjectが重力の影響ですぐに落ちてしまいます。
 このほかには、左右の前方にあるいくつか組み合わさった箱です。これらにはプレイヤーがぶつかったり、ジャンプで登ったり、あるいはSeekObjectがプレイヤーの後についてきて一緒に登ったりします。
 以下はその構築コードです。
void FixedBox::OnCreate() {
    auto PtrTransform = GetComponent<Transform>();
    PtrTransform->SetScale(m_Scale);
    PtrTransform->SetRotation(m_Rotation);
    PtrTransform->SetPosition(m_Position);
    //OBB衝突j判定を付ける
    auto PtrColl = AddComponent<CollisionObb>();
    PtrColl->SetFixed(true);
    //タグをつける
    AddTag(L"FixedBox");
    //影をつける(シャドウマップを描画する)
    auto ShadowPtr = AddComponent<Shadowmap>();
    //影の形(メッシュ)を設定
    ShadowPtr->SetMeshResource(L"DEFAULT_CUBE");
    auto PtrDraw = AddComponent<BcPNTStaticDraw>();
    PtrDraw->SetMeshResource(L"DEFAULT_CUBE");
    PtrDraw->SetTextureResource(L"SKY_TX");
    PtrDraw->SetFogEnabled(true);
    PtrDraw->SetOwnShadowActive(true);

}
 このようにCollisionObb衝突判定のみ付けます。重力の影響は受けないのでGravityは付けません。
 動くことはないのでコリジョンに、PtrColl->SetFixed(true);という属性を付けます。
 PtrColl->SetFixed(true);というのは動かないという属性ではありません。例えばこのボックスが行ったり来たりの運動をしている場合でも、例えばプレイヤーがぶつかってもその影響は受けないという意味です。
 そのあたりの属性に対する説明も、のちのサンプルで説明します。

まとめ

 このように、このサンプルにはプレイヤー含め4種類のクラスが実装されています。
 同じクラスのオブジェクトは、C++のインスタンス機能により複数作成することができます。

 このサンプルはコリジョンと重力のみで世界を表現しています。
 正確な物理計算をしているわけではないので、例えばMoveBoxは小突かれて移動しても回転をするこちはありません。
 しかし、多くのゲームの場合、正確な物理計算を必要とするものではありません。例えば横スクロールの3D世界の場合は、かえって物理計算が負担になる場合もあります。
 また、物理法則に合わない表現をしたい場合があります。ゲームの世界はそれっぽく見えることが重要で、物理法則が重要でない場合もおいです。
 また計算の負荷も間違いなく物理世界を表現するほうが負荷が大きくなります。

 次項では、物理計算を使った3Dの世界を紹介します。このサンプルとの違いを確認しましょう。