5

私は、多数の異なるエンティティを特徴とするゲーム環境に取り組んでいます。それぞれにいくつかの共通機能 (Draw、Update など) がありますが、ゲームは敵のタイプに基づいてそれらを異なる方法で処理する必要がある場合があります。これまでのところ、敵の「タイプ」をインスタンスのクラスにエンコードしました。したがって、次のような状況があります。

class MotionObject { ... };

class Entity : public MotionObject { ... };

class Coin : public Entity { ... };

class TextSign : public Entity { ... };

class ShapeEnemy : public Entity { ... };

class Attractor : public ShapeEnemy { ... };

class Bumper : public ShapeEnemy { ... };

したがって、クラス Coin、TextSign、Attractor、および Bumper は、ゲームでインスタンス化されるエンティティのタイプです。エンティティの種類ごとに異なるクラスを持つことは正しいと感じますが、「エンティティの種類」に基づいて動作を制御する switch ステートメントを含むエンティティ クラスが 1 つだけあれば、多少の煩わしさを回避できるかもしれないという感覚を揺るがすことはできません。変数に格納されたもの。プレーヤーは、タイプに応じてさまざまな方法でこれらのエンティティとやり取りします。動的キャストと null テストを毎回使用して、どの動作が適用されるかを判断します。明確にするために、これらは各エンティティで単純な Update() を呼び出すことができない動作のためのものです。プレーヤーは特定の方法で応答するか、エンティティ間の相互作用になります。これらはすべてエンティティ タイプに基づいています。私のコードは次のようになります。

void Game::Update(float deltaT) {

    for (int i =0; i < DrawChildren.size(); i++) {
        //each entity has its Update called
        DrawChildren[i].Update(deltaT);

        //What follows is code that the Game class needs to run based on the entity type.
        Coin * coin = dynamic_cast<Coin*>(DrawChildren[i]);
        if (coin != nullptr) {
            ...
            continue; //so no other type's code executes, in case I have inherited types.
        }

        TextSign * textSign = dynamic_cast<TextSign*>(DrawChildren[i]);
        if (textSign != nullptr) {
            ...
            continue; //so no other type's code executes, in case I have inherited types.
        }

        Attractor * attractor = dynamic_cast<Attractor*>(DrawChildren[i]);
        if (attractor != nullptr) {
            ...
            continue; //so no other type's code executes, in case I have inherited types.
        }

        Bumper * bumper = dynamic_cast<Bumper*>(DrawChildren[i]);
        if (bumper != nullptr) {
            ...
            continue; //so no other type's code executes, in case I have inherited types.
        }

    }

    ...

}

これを行うためのより面倒な方法はありますか?

4

4 に答える 4

1

(この質問は最近注目を集めているようだったので、私が自分で使い始めた解決策を提供します)。

私はEntity-Component Systemに切り替えましたが、それは素晴らしいものでした。コンポーネントをどのレベルの粒度で作成するかを理解するのは最初は困難でしたが、数か月の作業を行った後、それを「理解」し、クラスの継承に戻ることは想像できませんでした。

私の ECS では、entityxを使用しています。C++11 に依存していて、最終的には Windows でビルドしたいので、少しイライラしましたが、見つけたすべての ECS エンジンの中で、私にとって最も自然な構文でした。

私の現在のコンポーネントの種類は次のとおりです。

ParentComponent
Transformable
TranslateBounds
Drawable
Sprite
SpriteAnimation //gif-style
Velocity
Tags //a list of identifying strings
Animations
于 2014-02-12T15:40:20.343 に答える
1

私の意見では、この問題は通常、インターフェースを使用して解決するのが最善です。オブジェクトが描画可能な場合は、IDrawable を実装します。移動可能な場合は、IMovable を実装します。更新できる場合は、IUpdatable を実装します。

オブジェクトを作成すると、オブジェクトを追加 (IOC) したり、さまざまなリストに自分自身を追加したりして、適切なタイミングで移動/更新/描画などを許可することができます。

この戦略はシンプルで柔軟性があり、非常に読みやすいコードにつながります。switch ステートメントまたは壊れやすいクラス階層を使用したいという衝動に抵抗してください。

はい、これは C# ではなく C++ であることはわかっているため、インフラストラクチャの構築にはもう少し作業が必要です。抽象クラス、データ メンバーのない多重継承、および dynamic_cast を使用して、同様の効果を得ることができます。古い学校の一部のメンバーは、実行時のオーバーヘッドを回避するためにビット フラグと静的キャストを使用することを好みます。

于 2014-02-12T14:13:13.177 に答える
0

サブクラス化するかサブクラス化しないかの選択は微妙なものです。エンティティ固有のコードは少量しかありませんか? 昔ながらの一般的なエンティティ クラスを考えてみましょう。それは、あなたが他の場所で何をしているか、そしてどのパターンが最も快適にフィットするかによって異なります.

あなたの場合、あるクラスに別のポリモーフィック クラスを識別させる必要がある場合は、

virtual EntityTypeEnum GetType();

それが何であるかを示す列挙型を返すメソッド。何度も型キャストしようとするよりも少しきれいで、switches loveenumです。でも基本的には同じパターンです。

于 2013-09-20T00:17:33.477 に答える