6

エンティティ コンポーネント システム (今後は ECS) の最速バージョンを C++ で実装する方法を知りたいです。

まず、用語について:

  • シーンは、エンティティ(および一部の実装ではシステム)のコンテナーです。
  • コンポーネントは単純なデータ ストレージです (位置、衝突ボックス、レンダリングするイメージなど)。
  • システムは、システムの要件に適合するコンポーネントでロジックを実行します (これは、物理、プレーヤー入力、単純なレンダリングなどである可能性があります)。
  • エンティティには、最終的な動作を構成するいくつかのコンポーネントが含まれています

私たちが思いついたすべてのデザインを以下にリストしました.


1.「素朴な」方法

シーンには、順序付けされていないすべてのエンティティが含まれています。
システムが更新されると、すべてのシステムがすべてのエンティティをループし、各エンティティに必要なすべてのコンポーネントが含まれているかどうかを確認してから、それらのエンティティに対して更新を実行する必要があります。

明らかに、この方法は、多数のシステムおよび/または多数のエンティティがある場合、あまりパフォーマンスが高くありません。


2.「ビットマスク列挙型」とマッピングの使用

各コンポーネントには、ビットマスクの形式でタイプ識別子が含まれています (例: 1u << 5/ binary [0...]100000)。その後、各エンティティはすべてのコンポーネントのタイプ識別子を構成できるため (すべてのタイプ ID がエンティティ内で一意であると仮定)、次のようになります。

1u << 5 | 1u << 3 | 1u << 1
binary [0...]101010

シーンには、システムが適切なエンティティを簡単に検索できるある種のマップが含まれています。

MovementSystem::update() {
    for (auto& kv : parent_scene_) { // kv is pair<TypeID_t, vector<Entity *>>
        if (kv.first & (POSITION | VELOCITY))
            update_entities(kv.second); // update the whole set of fitting entities
    }
}

長所

  • 単純な方法よりも高速

短所

  • システムは、更新されるたびに適切なエンティティを検索する必要があります。
  • ビットマスク (列挙型) はビット数 ( の場合は 32、 のuint32_t場合は少なくとも 64 unsigned long long) に制限されており、場合によっては、ビットマスクが許可するよりも多くのコンポーネントが必要になることがあります。

3. システムを使用しない

この方法については、Danvilが以下の回答で説明しています。

長所

  • ビットマスクのことを完全に取り除きます。
  • 設計 2 よりも高速になる可能性があります。

短所

  • に依存しdynamic_castてコンポーネントを検索しますが、設計 #2 はコンポーネントを直接検索してから安全に検索できますstatic_cast

4.予備セットの使用

この方法は、以下の回答でskypjackによって説明されています。彼は彼のアプローチを非常に詳細に説明したので、彼の答えを読むことをお勧めします.

4

2 に答える 2

0

あなたが「システム」と呼んでいるものは、実際にはコンポーネントだと思います。レンダリングの例: コンポーネントPose(3D 位置回転用) とコンポーネントMesh(頂点バッファーを保持) があります。特定のエンティティをレンダリングできるかどうかをチェックする関数を用意する代わりに、 component を追加しますRenderer。このコンポーネントはPoseおよびMeshコンポーネントに接続します。「システム」レンダリングは、コンポーネントとのみ通信する必要がありますRenderer。また、各エンティティはレンダリング可能またはレンダリング可能であり、コンポーネントを毎回チェックする必要がなく、レンダリングのためのすべての作業がコンポーネントとして収集されます。


コード例:

struct Pose : public Component { float x,y; };

struct Mesh : public Component { std::vector<Vertex> vertices; };

struct Renderer : public Component {
   Entity* entity;
   void render() {
       if(!mesh|| entity->componentsChanged) {
           mesh = entity->getComponent<Mesh>();
           if(!mesh) throw error;
       }
       if(!entity->pose) throw error;
       glTranslate(entity->pose->x, entity->pose->y);
       ...
   }
private:
   Mesh* mesh;
};

struct Entity {
    std::vector<Component*> components;
    bool componentsChanged;
    template<typename C> C* getComponent() const {
        for(Component* c : components) {
            C* cc = dynamic_cast<C>(c);
            if(cc) return cc;
        }
        return NULL;
    }
    // "fast links" to important components
    Pose* pose;
    Renderer* renderer;
    PhysicsStuff* physics;
};

struct Rendering
{
private:
    void render(const std::vector<Entity*>& entities) {
        for(Entity* e : entities) {
            if(!e->renderer) continue;
            e->renderer->render();
        }
    }
};    
于 2014-05-18T13:59:06.267 に答える