
エンティティコンポーネントシステム(ECS)は、ソフトウェアアーキテクチャパターンの一つです。ECSは、データコンポーネントで構成されるエンティティと、それらのコンポーネントを操作するシステムから成ります。ゲーム世界のオブジェクト表現において、ビデオゲーム開発で最もよく用いられます。
ECSは継承よりも構成を優先します。すべてのエンティティは型階層ではなく、それに関連付けられたコンポーネントによって定義されます。システムは、必要なコンポーネントを持つすべてのエンティティに対してグローバルに作用します。たとえば、食料システムは、空腹度を追跡する関連コンポーネントを持つすべてのエンティティを順に処理し、一定時間間隔で満腹状態から少し遠ざけるように作用します。地形やアイテムなど、そのコンポーネントを持たないエンティティは、当然ながら食料システムによって無視されます。
英語の曖昧さのため、ECS という名称は、エンティティとコンポーネントで構成されるシステムであると解釈されることがあります。2002 年の GDC での講演で、[ 1 ] [ 2 ] Scott Bilas は、C++ オブジェクト システムと彼が新たに作成したカスタム コンポーネント システムを比較しました。これは、一般的なシステム エンジニアリングにおけるシステム用語の伝統的な使用法と一致しており、例としてCommon Lisp オブジェクト システムや型システムが挙げられます。
ECSは主にビデオゲーム開発で見られますが、 Gazeboのようなロボットシミュレータなど、他の分野でも役立ちます。 [ 3 ] [ 4 ]
ECSは、一般的なコンピュータサイエンスとプログラミング言語理論における直交的で確立されたアイデアを組み合わせたものです。たとえば、コンポーネントはさまざまなプログラミング言語におけるミックスインイディオムと見なすことができます。コンポーネントは、一般的な委譲アプローチとメタオブジェクトプロトコルの下での特殊なケースです。つまり、完全なコンポーネントオブジェクトシステムは、オブジェクト指向プログラミングのオーランド条約[ 5 ]のビジョン内のテンプレートと共感モデルを使用して表現できます。
エンティティの動作は、コンポーネントを追加、削除、または変更するシステムによって実行時に変更できます。これにより、オブジェクト指向プログラミング手法によく見られる、理解、保守、拡張が困難な深くて広い継承階層の曖昧さの問題が解消されます。一般的なECSアプローチは、データ指向設計手法と高い互換性を持ち、しばしば組み合わせて使用されます。コンポーネントのすべてのインスタンスのデータは、物理メモリに連続して格納されるため、多数のエンティティを扱うシステムでも効率的なメモリアクセスが可能になります。
1963年、イヴァン・サザーランドのSketchpadは、初期のECSを使用して描画の視覚要素を保存しました。点を異なるオブジェクト(線、円、長方形など)にカプセル化する代わりに、点はリングバッファに格納され、視覚要素はそれらを参照するだけでした。点を移動すると、それを使用するすべての形状と制約を更新できました。[ 7 ]
1998年、『Thief: The Dark Project』はECSを先駆的に導入した。[ 8 ]このエンジンは後に続編や『System Shock 2』にも使用された。
2002年、Gas Powered Games(Dungeon Siege)のスコット・ビラスがECSに関する画期的な講演を行った。[ 1 ]これが、後に数多くの有名な実装に影響を与えた。
2007年1月初旬、トニー・ホークシリーズの開発に携わったミック・ウェストは、NeversoftでのECS導入プロセスに関する自身の経験を共有した。[ 9 ]
また、2007年には、 Operation Flashpoint: Dragon Risingの開発チームが、Bilas/ Dungeon Siegeに影響を受けたものを含む ECS 設計を実験し、アダム・マーティンは後に、コア用語と概念の定義を含む ECS 設計の詳細な説明を執筆しました。[ 10 ] [ 11 ]特に、マーティンの研究は、システムを第一級要素、エンティティを識別子、コンポーネントを生データ、コードをコンポーネントやエンティティではなくシステムに格納するという考え方を普及させました。
2015年、Apple Inc.はiOS、macOS、tvOSのゲーム開発のためのAPIフレームワークであるGameplayKitを発表しました。これにはECSの実装が含まれています。[ 12 ]
2018年10月[ 13 ]、 Unity社はECS上に構築された技術スタックを利用したメガシティのデモをリリースしました。UnityのECSはDOTSと呼ばれる強力で最適化されたアーキテクチャ上で動作し、「クリエイターが高性能な方法で処理を拡張できるようにする」ものです。
異なるECSのデータレイアウトは、コンポーネントの定義、エンティティとの関連性、システムがエンティティのコンポーネントにアクセスする方法など、それぞれ異なる場合があります。
アダム・マーティンは自身のブログシリーズで、彼が考えるエンティティ・コンポーネント・システムとは何かを定義している。[ 11 ]
エンティティは、コンポーネントにアクセスするためのIDのみで構成されます。各エンティティに一意のIDを使用するのが一般的です。これは必須ではありませんが、いくつかの利点があります。
これらの利点のいくつかは、スマートポインターを使用することによっても実現できます。
コンポーネントにはゲームコード(動作)は含まれていません。コンポーネントはエンティティと物理的に同じ場所に配置されている必要はありませんが、エンティティを使用して簡単に見つけてアクセスできる必要があります。
「各システムは(あたかも各システムが独自のプライベートスレッドを持っているかのように)継続的に実行され、そのシステムのクエリに一致するコンポーネントを所有するすべてのエンティティに対してグローバルなアクションを実行します。」
Unityのレイアウトはテーブルで構成され、各テーブルにはコンポーネントの列があります。このシステムでは、エンティティタイプはそれが保持するコンポーネントに基づいています。すべてのエンティティタイプには、そのエンティティで使用されるコンポーネントに一致するコンポーネントの列を保持するテーブル(アーキタイプと呼ばれます)があります。特定のエンティティにアクセスするには、適切なアーキタイプ(テーブル)を見つけ、各列にインデックスを付けて、そのエンティティに対応する各コンポーネントを取得する必要があります。
システム間でデータを送信する一般的な方法は、データをコンポーネントに格納し、各システムがそのコンポーネントに順次アクセスすることです。たとえば、オブジェクトの位置を定期的に更新することができます。この位置は、他のシステムで使用されます。発生頻度の低いさまざまなイベントが多数ある場合、1つ以上のコンポーネントに多数のフラグが必要になります。システムは、これらのフラグをイテレーションごとに監視する必要があり、非効率になる可能性があります。解決策として、オブザーバーパターンを使用する方法があります。イベントに依存するすべてのシステムがそのイベントを購読します。これにより、イベントからのアクションは、イベントが発生したときに一度だけ実行され、ポーリングは不要になります。
ECSは、コンポーネントが単純なデータバケットであり依存関係を持たないため、オブジェクト指向プログラミングでよく見られる依存関係の問題に悩まされることはありません。各システムは通常、エンティティがシステム上で操作するために必要なコンポーネントのセットを照会します。たとえば、レンダリングシステムは、モデル、変換、および描画可能なコンポーネントを登録します。システムが実行されると、これらのコンポーネントをすべて持つエンティティに対してロジックが実行されます。その他のエンティティは単純にスキップされ、複雑な依存関係ツリーは必要ありません。ただし、コンポーネントを介してあるシステムから別のシステムに値を伝播させるとデバッグが難しくなる可能性があるため、バグが隠れる場所となる可能性があります。ECSは、結合されていないデータを特定のライフタイムにバインドする必要がある場合に使用できます。
ECSは継承ツリーではなく、コンポジションを使用します。エンティティは通常、IDとそれに付随するコンポーネントのリストで構成されます。適切なコンポーネントをエンティティに追加することで、あらゆるゲームオブジェクトを作成できます。これにより、開発者は依存関係の問題なく、エンティティに機能を簡単に追加できます。たとえば、プレイヤーエンティティに弾丸コンポーネントを追加すると、その弾丸はbulletHandlerシステムによって操作される要件を満たし、その結果、弾丸が他のオブジェクトに衝突してダメージを与えることができます。
ゲームの状態を保存するために ECS を使用することの利点は、アダム・マーティンのような多くのゲーム開発者によって主張されています。良い例の 1 つは、リチャード・ロードのブログ記事で、彼は ECS で設計されたゲームデータストレージシステムの利点と、それがなぜ非常に有用なのかを論じています。[ 14 ]