Entity-Component-System architecture の哲学を研究しています。私がそれについて読んだように、典型的なエンティティシステムには以下があります:
1)Entities-いくつかのコンポーネントを持つ単なるIDタグです
2)Components-コンポーネントが担当するエンティティのさまざまな側面に関するdataを含みます
3)Systems-すべてのエンティティの関連コンポーネントを更新します。たとえば、レンダリングシステムがレンダリングコンポーネントを更新する、または単に言うと、そのコンポーネントのデータに格納されている画像を描画します。位置および移動システムは、対応するコンポーネントを持つ各エンティティの位置と移動を処理します。
これらのステートメントは この記事 の後に続きます。
しかし、著者はシステム間の相互作用がどのように実現されるべきかを説明しませんでした。たとえば、レンダリングシステムは、エンティティを正しい位置に描画するために、エンティティの位置コンポーネントからのデータを知っている必要があります。等々。
だから問題は-さまざまなシステム間の相互作用をどのように実装すべきですか?
だから問題は-さまざまなシステム間の相互作用をどのように実装すべきですか?
理想的には、直接的な意味ではなく、相互作用しないことです。 ECS内のシステムはすべて、中央のECSデータベースにアクセスでき、そこに接続されているエンティティとコンポーネントをフェッチできます。彼らは互いに直接話しません。これらはデータベースと通信し、すべて互いに独立して実行されます。
依存関係は抽象化ではなく生データに向かって流れる
ECSの依存関係は関数に流れず、抽象的な機能にさえ流れません。それらはすべてraw dataに向かって流れます。これは、受け入れられている多くのソフトウェアエンジニアリング原則の壮大な違反のように聞こえるかもしれません。ソフトウェアエンジニアリングの原則の一部が間違っているか、少なくともすべてのシナリオに適用できるとは限りません。インターフェイスやデザインの安定性よりもデータの安定性を実現する方が簡単な状況は数多くあります。基本的な例として、生の行列コンポーネントが一度にすべてのデータフィールドを持つべきであるという理由を推論し、今後数年間その(変化しない)安定性を維持する方がはるかに簡単です。抽象IMatrix
インターフェースがすべての機能を提供し、何年にもわたって完全に安定した(不変の)状態を保ち、完全な安定性(変化しない)を維持する必要がある場合は、追加や削除などの誘惑に直面することはありません。関数を変更します。
したがって、適切なケースでは、依存関係が抽象的な機能ではなくデータに向かって流れる場合、コードベースは、カスケード効果と潜在的に大きなパーツの書き換えが必要な中央設計の変更に直面する理由がますます少なくなります。その場合、依存関係をデータに向けることは、それらを安定性に向けることです。システムの傾向として、開発者が関数を追加、変更、削除するのか、それともコンポーネントにデータを追加、変更、削除するのか、開発者として自問自答してください。前者の場合は、ECSエンジンの恩恵を受ける可能性があります。
システムが相互に大きく依存し始めた場合、依存関係はデータから機能へと向かい、メンテナンスの利点の多くと、エンジンの正確性を推論し、設計レベルで容易に安定させることができる機能が失われます。 。もちろん、実用的なソリューションでは、たまに別のシステムで関数を呼び出すシステムが必要になる場合がありますが、通常は最小限に抑えるように努める必要があります。互いに直接対話する代わりに、他のシステムがそれらの変更を取得してそれに応じて対応できるように、システムにコンポーネントを変更してエンティティにアタッチさせることができます。
システムの相互作用
[...]レンダリングシステムは、エンティティを正しい位置に描画するために、エンティティの位置コンポーネントからのデータを認識している必要があります。等々。
ECSデータベースから取得でき、レンダリングコンポーネントと位置コンポーネントを持つエンティティをループします。これは、物理コンポーネントが位置コンポーネントを持つエンティティをループして位置を変更する前と同じです。通常、各システムは基本的なループモデルに適合します。
for each entity with the components I'm interested in:
do something with the components
...そして、直感的な解決策がすべてを1つのパスで実行することである場合でも、パスで、しばしば複数のパスで物事を行うことについて考え始める必要があります。たとえば、すべてのゲームエンティティをループして物理を適用し、入力に応答してAIを処理し、それらをすべて一度にレンダリングすると、より直感的になります。これにより、ループの量を最小限に抑えることができ、必要な状態も少なくなります。ただし、ECSは通常、これを複数の単純なパスで処理し、場合によっては、あるパスから次のパスで使用する中間状態を少し増やしますが、トレードオフとして、システムの保守が非常に簡単になり、変更や変更が簡単になります。並列化*およびベクトル化の可能性があります。
- ユーリが述べたように、少なくともシステム間でシステム間で並列化を困難にすることもできますが、並列ループの正確さについて推論することは容易ではないため、システム内で並列化を容易にすることができますたとえば、途中で状態の変更が少なく、パスに含まれるコードがはるかに簡単な場合は、ロックします。私の率直な意見では、多くの場合、システム自体をマルチスレッド化することは、パフォーマンスが最も重要なシステムの内部で実行しているループほど重要ではありません。
複数の単純なパス
GPUは反復ごとに複雑なことを行うのが得意ではないため、GPUプログラミングにいくらか似ています。そのため、反復ごとに単純なことを実行する代わりに、複数の単純な同じデータを繰り返し実行した後、複雑なタスクになるExcelがよく使用されます。パス。
GPUプログラミングとは異なり、1回のパスでさらに複雑なことを実行できる可能性がありますが、各パスは1つの論理的な考え方のように表されます:「これらの各コンポーネントについて、物理学を適用するレンダリング。物理システムは、独自の孤立した世界に住んでいるレンダリングシステムが独自の完全に分離された取り外し可能なレンダリングパスを実行するのと同じように、独自のパスを実行します。各システムは独自の小さな世界に住んでおり、ECSデータベースのみを見て、内部のコンポーネントとエンティティを取得できます。彼らは他のシステムが何をしているのか気にする必要はありません。
実際、適切に設計されたECSでは、システムが機能に相互に依存していないため、エンジンからシステムを削除しても、コードベース自体がひどく崩壊することはありません。関心のあるのは、中央データベースと、処理に関心のあるコンポーネント(生データ)だけです。彼らはすべて自分の孤立した世界に住んでいます。その結果、ゲームから物理システムを削除できるはずです。その時点で、モーションコンポーネントは物理を適用しなくなりますが、他のすべては以前と同じように機能し続けるはずです。その点で非常に直交しています。
イベント駆動型プログラミング
イベント駆動型プログラミングはECSで少し扱いにくい場合がありますが、それを解決する簡単な方法の1つは、イベントキューコンポーネントを用意することです。システムは、最初のシステムが2番目のシステムの関数を直接呼び出すことなく、別のシステムが遅延してポップおよび処理するように、これらのキューコンポーネントにイベントをプッシュできます。繰り返しになりますが、相互作用の大部分は、システム->システムではなく、システム-> ECS、およびシステム->コンポーネントである必要があります。
まだ質問していない質問への簡単な回答:エンティティIDタグは、テーブルのデータベース行のキーのようなものです。そのキーを使用してシステムにクエリを実行し、必要な情報を取り出します。
はい、それはポインタを離れて格納するよりも理論的に遅いです。いいえ、システムはそのキーO(1)を検索できるため、思ったほど遅くはありません。
まだ尋ねていない質問に対するもう1つの簡単な答え:はい、誰もが最近愛しているこれらのコンポーネントシステムは、スムーズに動作させるためにインフラストラクチャの作業が必要です-データが格納されている場所の再調整として存在するわけではありません=)TANSTAAFL
システムとコンポーネントは1:1の関係に制限されていませんが、同期を必要とせずに並列処理を可能にする望ましい特性です。一般に、システムはエンティティに接続されたコンポーネントをいくつでも操作できます。たとえば、次の一般的な設定を考えてみます。
部品
システム(動作するコンポーネントを含む)
これにより、すべてのシステムが必要なすべてのコンポーネントにアクセスできるようになります。システム間通信は必要ありません(実際、この通信はコンポーネントを介して暗黙的に行われます)が、並列化は犠牲になります。