12.Update系の操作

1209.経路探索

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

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

 

図1209a

 

 ここで、プレイヤーをマス目の中に移動します。すると、奥にいる赤い球(敵です)が、プレイヤーを追いかけてきます。
 その際、障害物をよけるように(多少ぶつかったりはしますが)追いかけてきます。
 プレイヤーがマス目の外に出ると、敵は、最初いた位置に戻ります。

グラフ探索

 このように、追いかける対象の位置が変化するのに合わせて、ルートを変えて探索するアルゴリズムをグラフ探索といいます。
 グラフ探索にはいろいろ方法はありますが、BaseCross64に実装されているのはA-Starという探索アルゴリズムです。
 このサンプルはこの実装方法を紹介したものです。

セルマップ

 ステージ上にあるマス目をセルマップといいます。AIをする対象の領地というか、探索する領域のようなものです。
 ステージ上にはセルマップは複数配置することができ、例えば、敵ごとに別々の領域を設定することも可能です(このサンプルでは敵は1体で、セルマップも一つです)。
 セルマップGameObjectです。ですからAddGameObject()関数により、以下のように設置します。
 以下、GameStage::CreateStageCellMap()関数ですが
//セルマップの作成
void GameStage::CreateStageCellMap() {
    float  PieceSize = 1.0f;
    auto Ptr = AddGameObject<StageCellMap>(Vec3(-12.0f, 0, 1.0f), PieceSize,26, 16);
    //セルマップの区画を表示する場合は以下の設定
    Ptr->SetDrawActive(true);
    //さらにセルのインデックスとコストを表示する場合は以下の設定
    //Ptr->SetCellStringActive(true);
    SetSharedGameObject(L"StageCellMap", Ptr);
    //セルマップへのボックスのコスト設定
    SetCellMapCost();
}
 ここでAddGameObjectのパラメータは注意が必要です。
Vec3(-12.0f, 0, 1.0f) セルマップの開始点
PieceSize 1ピースのサイズ
26 横方向(X方向)の数
16 奥方向(Z方向)の数
 となります。またここでは
    //セルマップへのボックスのコスト設定
    SetCellMapCost();
 とコストを設定します。以下コードです。
void GameStage::SetCellMapCost() {
    //セルマップ内にFixedBoxの情報をセット
    auto PtrCellmap = GetSharedGameObject<StageCellMap>(L"StageCellMap");
    auto BoxGroup = GetSharedObjectGroup(L"FixedBoxes");
    //セルマップからセルの配列を取得
    auto& CellVec = PtrCellmap->GetCellVec();
    //ボックスグループからボックスの配列を取得
    auto& BoxVec = BoxGroup->GetGroupVector();
    vector<AABB> ObjectsAABBVec;
    for (auto& v : BoxVec) {
        auto FixedBoxPtr = dynamic_pointer_cast<TilingFixedBox>(v.lock());
        if (FixedBoxPtr) {
            auto ColPtr = FixedBoxPtr->GetComponent<CollisionObb>();
            //ボックスの衝突判定かラッピングするAABBを取得して保存
            ObjectsAABBVec.push_back(ColPtr->GetObb().GetWrappedAABB());
        }
    }
    //セル配列からセルをスキャン
    for (auto& v : CellVec) {
        for (auto& v2 : v) {
            for (auto& vObj : ObjectsAABBVec) {
                if (HitTest::AABB_AABB_NOT_EQUAL(v2.m_PieceRange, vObj)) {
                    //ボックスのABBとNOT_EQUALで衝突判定
                    v2.m_Cost = -1;
                    break;
                }
            }
        }
    }
}
 それぞれのセルに何も障害物がなければ、コストは、1になります(デフォルトです)。例えばでこぼこの道だった場合は、2とかにします。
 ここでは障害物のボックスのコストを設定します。完全に通れないセルと設定する場合は-1にします。
 また、ここではボックスを検索してコストを設定しますので、ボックスがすでにステージに配置されていなければなりません。

探索アルゴリズムの設定

 セルマップを設置したら、敵に探索のアルゴリズムを設置します。以下、Enemy::OnCreate()関数です。
void Enemy::OnCreate() {
    //初期位置などの設定
    auto ptr = GetComponent<Transform>();
    ptr->SetScale(0.5f, 0.5f, 0.5f);
    ptr->SetRotation(0.0f, 0.0f, 0.0f);
    ptr->SetPosition(m_StartPosition);
    //CollisionSphere衝突判定を付ける
    AddComponent<CollisionSphere>();
    auto MapPtr = m_CelMap.lock();
    if (MapPtr) {
        AddComponent<PathSearch>(MapPtr);
    }
    //影をつける(シャドウマップを描画する)
    auto shadowPtr = AddComponent<Shadowmap>();
    //影の形(メッシュ)を設定
    shadowPtr->SetMeshResource(L"DEFAULT_SPHERE");
    //描画コンポーネントの設定
    auto ptrDraw = AddComponent<BcPNTStaticDraw>();
    ptrDraw->SetFogEnabled(true);
    //描画するメッシュを設定
    ptrDraw->SetMeshResource(L"DEFAULT_SPHERE");
    ptrDraw->SetDiffuse(Col4(1.0f, 0, 0, 1.0f));
}
 m_CelMapというのはセルマップのweak_preです。敵のコンストラクタでステージから渡しておきます。
 そのm_CelMapを使って、PathSearchというコンポーネントを追加します。これが探索アルゴリズムをコンポーネント化したものです。
 実際の探索は、Enemy::OnUpdate()で行います。ここから、いくつかのメンバ関数を呼んでいるわけですが、実際に探索するのはEnemy::Search()関数です。この関数はEnemy::SeekBehavior()関数から呼ばれます。探索結果によって、その場所にSeekするかArriveするかを決定します。
 SeekArriveは、1204.さまざまなステアリングでも紹介しています。
 ステアリングを実行するのにはフォースと速度が必要です。敵クラスは、それぞれm_Force、m_Velocityというメンバ変数を持っています。それらを前提にして、以下Enemy::OnUpdate()ですが
void Enemy::OnUpdate() {
    m_Force.setAll(0.0f);
    auto PlayerPtr = GetStage()->GetSharedGameObject<Player>(L"Player");
    auto PlayerPos = PlayerPtr->GetComponent<Transform>()->GetPosition();
    PlayerPos.y = GetStartPosition().y;
    if (SeekBehavior(PlayerPos) == CellSearchFlg::Failed) {
        if (SeekBehavior(GetStartPosition()) == CellSearchFlg::Arrived) {
            ArriveBehavior(GetStartPosition());
        }
    }
    float ElapsedTime = App::GetApp()->GetElapsedTime();
    m_Velocity += m_Force * ElapsedTime;
    auto EnemyPos = GetComponent<Transform>()->GetPosition();
    if (length(EnemyPos - PlayerPos) <= 1.8f) {
        m_Velocity *= 0.95f;
    }
    auto Pos = GetComponent<Transform>()->GetPosition();
    Pos += m_Velocity * ElapsedTime;
    GetComponent<Transform>()->SetPosition(Pos);
}
 のようになります。