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