EnTTを使ってみる(Unityライクな実装)

EnTTを使ってみる(Unityライクな実装)_アイキャッチ
目次

はじめに

今回はUnityを触っているとよく聞く「ECS」というものをC++で気軽に試してみようと思います。

ECSとは

ECSとはEntity Component Systemのことで設計パターンの1つです。Entity(オブジェクトID)にComponent(部品データなどの機能)を付け足し、System(振る舞い、Updateなど)で動かします。

このような設計はゲーム制作でよくある仕様変更に柔軟に対応できるようになります。

環境設定

  • Visual Studio 2022
  • C++20

実装開始

それでは実際に作ってみましょう。

今回は「EnTTを使ってみる」ということでEnTTのライブラリを使用します。

プロジェクトのプロパティ設定

まずはプロジェクトを作成してください。EnTTはC++17以上でしか動かないのでプロジェクト→プロパティ→全般からC++言語標準のC++バージョンを17以上にしてください。自分はC++20にしています。

プロパティ設定画面

設定できたら適用を押して今度はEnTTライブラリをインクルードしましょう。今回はヘッダーを一つインクルードするだけで使用できるhttps://github.com/skypjack/entt/blob/master/single_include/entt/entt.hppを使用します。どこに置いてもいいのでインクルードしてください。

EnTTを使ってサンプルを作成し実行してみる

できたらmain.cppを作成し、以下のソースコードをコピーしてください。

#include "entt/entt.hpp"
#include <iostream>

struct TransformComponent
{
    float pos_x, pos_y, pos_z;

    TransformComponent(float x = 0, float y = 0, float z = 0) : pos_x(x), pos_y(y), pos_z(z) {}
};

struct MeshComponent
{
    int meshNo;

    MeshComponent(int no = 0) : meshNo(no) {};
};

void Update(entt::registry& registry)
{
    for (auto [entity, transform, mesh] : registry.view<TransformComponent, MeshComponent>().each())
    {
        transform.pos_x += 1;
        transform.pos_y += 2;
        transform.pos_z += 3;
        std::cout << "entity: " << (uint32_t)entity << std::endl;

        std::cout << "Transform: " << transform.pos_x << ", " << transform.pos_y << ", " << transform.pos_z << std::endl;
        std::cout << "MeshNo   : " << mesh.meshNo << std::endl;
    }
}

int main()
{
    entt::registry registry;

    for (size_t entityNo = 0; entityNo < 10; ++entityNo)
    {
        // エンティティ作成(Entity)
        entt::entity entity = registry.create();
        // コンポーネント追加(Component)
        registry.emplace<TransformComponent>(entity, 1.0f, 2.0f, 3.0f);
        registry.emplace<MeshComponent>(entity, entityNo);
    }

    // レジストリの中のエンティティを更新(System)
    Update(registry);

    return 0;
}

実際やっていることは

  1. エンティティを作成
  2. コンポーネントを追加
  3. Update関数で更新

を行っています。

1.エンティティを作成

entt::entity entity = registry.create();

↑これでEntityを作成しています。Entityはentt.hppの中身を見ると”enum class entity : id_type {};”となっています。普通に0,1で表現されるIDです。この段階ではIDを作成しただけですのでTransformやMeshなどのコンポーネント、nameやTagすらありません。

2.コンポーネントを追加

registry.emplace<TransformComponent>(entity, 1.0f, 2.0f, 3.0f);

Unityで例えるとAddComponentのようなものです。EntityにTransformを作成して紐づけています。

3.コンポーネントが紐づいているEntityの更新

for (auto [entity, transform, mesh] : registry.view<TransformComponent, MeshComponent>().each())

Viewを使用して特定のコンポーネントが紐づいているEntityを見つけて更新しています。今回はTransformとMeshコンポーネントの両方が紐づいているEntityを更新しています。なのでTransformのみ、Meshのみ紐づいているEntityは更新されません。

まとめ

いかがでしたか?うまく動いてECSがなんとなく理解できたでしょうか?次回はここからもう少し手を加えてUnityのような操作感(GameObjectにAddComponentする)ができるようにしてみたいと思います。

目次