9
9

More than 5 years have passed since last update.

Entity Component System

Posted at

この記事について

Entity Component System(以後ECS)について解説します。また、ECSのC++での実装EnTTについて紹介します。

Entity Component Systemについて

Entity Component Systemは設計パターンの一つです。継承よりも委譲を優先する原則に従い、
エンティティ(ゲームシーンを構成する敵キャラ、ドア、弾丸、etc...)を部品を組み合わせることで実装できるようにします。この手法は長い継承関係による不透明な実装を回避して、設計をクリーンにします。一方で、実行時のオーバーヘッドという短所もあります。

ECSは以下の3つの要素からなります。

  1. Entity
    コンポーネントを追加するコンテナです。通常、階層構造をとっています。(通常、Entityは SubEntityを持ちます。)
  2. Component
    オブジェクトの振る舞い、見た目、データを定義するクラスです。
  3. System
    SystemはEntityとComponentを利用して、データに基づいた振る舞いとロジックを保持します。

UMLで表すとこんな感じです。
Entityに対して複数のComponentが紐付けられます。

ECS.png

Systemは、Entityを介してEntityの構成要素のComponentにアクセスできます。また、Entityを介さずに全てのComponentに対してループできたりします。

ECSは従来のOOP(オブジェクト指向)のアプローチに比べて以下のメリットがあります。
1. EntityはポインタではなくIDとして保持できます。このことによりダングリングポインタ(指している先が無効な状態になっているポインタ)が発生しません。
2. 外部に状態を保持しやすなります。状態を読み込むときにポインタを再構築する必要がありません。
3. メモリ上のデータの位置を再配置できます。
4. ポインタを使用しないので、ネットワークを介してEntityを送受信できます。

やってみよう!!

上を読んでみてECSで遊んでみたくなったと思います。早速、C++でのECSの実装のEnTTを使ってECSを実装してみましょう。

Sample.cpp
#include <entt/entt.hpp>
#include <cstdint>
struct position {
    float x;
    float y;
};
struct velocity {
    float dx;
    float dy;
};

// Systemに当たる部分。Entityの位置を更新する。
void update_position(std::uint64_t dt, entt::registry &registry) {
    registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
        // gets all the components of the view at once ...
        pos.x += vel.dx * dt;
        pos.y += vel.dy * dt;
        // ...
    });
}
int main() {
    entt::registry registry;
    std::uint64_t dt = 16;
    for(auto i = 0; i < 10; ++i) {
        auto entity = registry.create();
        registry.assign<position>(entity, i * 1.f, i * 1.f);
        if(i % 2 == 0) { registry.assign<velocity>(entity, i * .1f, i * .1f); }
    }
    update_position(dt, registry);
}

描画したいときは新しいSystemとComponentを追加すればできます。

draw.cpp
struct Drawable {
    Image image; // 描画に使う画像
};
void update_drawing(entt::registry& registry)
{  
    registry.view<Drawable, const position>().each([dt](auto &pos, auto &vel) {
      // 描画に使うロジック
    });
}

参考文献

9
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9