コンポーネントエンティティシステムアーキテクチャを使用してアプリケーション(ゲームではない)を構築するのは妥当ですか?


24

Apple AppStoreやGoogle Playアプリストアなどのアプリケーション(ネイティブまたはWeb)を構築するとき、Model-View-Controllerアーキテクチャを使用することは非常に一般的であることを知っています。

ただし、ゲームエンジンで一般的なComponent-Entity-Systemアーキテクチャを使用してアプリケーションを作成することは合理的ですか?


1
Light Tableのアーキテクチャをチェックしてください:chris-granger.com/2013/01/24/the-ide-as-data
ハカンデリアール

回答:


39

ただし、ゲームエンジンで一般的なComponent-Entity-Systemアーキテクチャを使用してアプリケーションを作成することは合理的ですか?

絶対に。私はビジュアルFXで仕事をしており、この分野のさまざまなシステム、そのアーキテクチャ(CAD / CAMを含む)、SDKに飢えている、そして無限に思えるアーキテクチャ上の決定の長所と短所を感じさせる論文を研究しました。最も微妙なものでさえ、常に微妙な影響を与えるとは限りません。

VFXは、レンダリングされた結果を表示するビューポートを持つ「シーン」という1つの中心的な概念があるという点で、ゲームにかなり似ています。また、物理コンテキストが発生している可能性のあるアニメーションコンテキスト、パーティクルエミッターがパーティクルをスポーンし、メッシュがアニメーション化およびレンダリングされる、モーションアニメーションなど、最終的にそれらをレンダリングするアニメーションコンテキストで絶えず回転している多くの中央ループ処理が行われる傾向があります最後にユーザーにすべて。

少なくとも非常に複雑なゲームエンジンに似たもう1つの概念は、デザイナーが独自の軽量プログラミング(スクリプトおよびノー​​ド)を行う能力など、シーンを柔軟に設計できる「デザイナー」アスペクトの必要性でした。

長年にわたって、ECSが最適であることがわかりました。もちろん、それは主観性と完全に離婚することは決してありませんが、私はそれが最も少ない問題を与えるように強く見えたと言うでしょう。それは、私たちが常に苦労していたより多くの大きな問題を解決しましたが、見返りにいくつかの新しいマイナーな問題だけを与えました。

従来のOOP

従来のOOPアプローチは、実装要件ではなく設計要件をしっかりと把握している場合に非常に強力になります。よりフラットな複数のインターフェイスアプローチまたはよりネストされた階層型ABCアプローチのいずれを使用する場合でも、設計を強化し、変更をより簡単かつ安全にしながら、変更をより困難にする傾向があります。単一のバージョンを超える製品には常に不安定性が必要であるため、OOPアプローチでは、安定性(変更の難しさと変更理由の欠如)を設計レベルにゆがめ、不安定性(変更の容易さと変更理由)を偏らせる傾向があります実装レベルまで。

ただし、進化するユーザーエンド要件に対しては、設計と実装の両方を頻繁に変更する必要がある場合があります。植物と動物の両方である必要があり、構築した概念モデル全体を完全に無効にしなければならない類推的な生物に対するユーザー側の強いニーズのような奇妙なものを見つけるかもしれません。通常のオブジェクト指向のアプローチでは、ここであなたを保護することはできません。また、そのような予期せぬ概念を壊すような変更をさらに困難にすることがあります。パフォーマンスが非常に重要な領域が関係する場合、設計変更の理由はさらに増大します。

複数の詳細なインターフェースを組み合わせてオブジェクトの適合インターフェースを形成することは、クライアントコードの安定化には大いに役立ちますが、サブタイプの安定化には役立ちません。たとえば、システムの一部でのみ使用される1つのインターフェイスを、そのインターフェイスを実装する1000の異なるサブタイプで使用できます。その場合、複雑なサブタイプ(それらが果たすべき非常に多くの異なるインターフェースの責任を持っているため複雑)を維持することは、インターフェースを介してそれらを使用するコードではなく悪夢になります。OOPは複雑さをオブジェクトレベルに転送する傾向がありますが、ECSはそれをクライアント(「システム」)レベルに転送します。これは、システムが非常に少なく、適合「オブジェクト」(「エンティティ」)がたくさんある場合に理想的です。

ここに画像の説明を入力してください

クラスはそのデータをプライベートに所有するため、不変式をすべて独自に維持できます。それにもかかわらず、オブジェクトが相互作用する場合、実際には維持するのが依然として困難な「粗い」不変条件があります。複雑なシステム全体が有効な状態になるには、個々の不変式が適切に維持されている場合でも、オブジェクトの複雑なグラフを考慮する必要があります。従来のOOPスタイルのアプローチは、詳細な不変式の維持に役立ちますが、オブジェクトがシステムの小さなファセットに焦点を合わせている場合、実際には、広くて粗い不変式を維持することを困難にします。

そこで、こうした種類のレゴブロック構築ECSアプローチまたはバリアントが非常に役立ちます。また、システムの設計が通常のオブジェクトよりも粗い場合、システムの鳥瞰図でこれらの種類の粗い不変式を維持することが容易になります。小さなオブジェクトの相互作用の多くは、1キロメートルの紙をカバーする依存関係グラフを備えた小さなタスクに焦点を合わせた小さなオブジェクトではなく、1つの広いタスクに焦点を合わせた1つの大きなシステムに変わります。

それでも、ECSについて学ぶには、ゲーム業界で自分の分野の外を見る必要がありましたが、私は常にデータ指向の考え方の1つでした。また、おもしろいことに、私は自分でECSに向かって進んでおり、繰り返して、より良いデザインを考え出そうとしています。しかし、私はそれを最後までやり遂げず、非常に重要な詳細を見落としました。これは、「システム」部分の形式化と、生データに至るまでコンポーネントを押しつぶすことです。

ECSにどのように落ち着き、以前の設計の反復ですべての問題を解決したのかを説明します。ここでの答えが非常に強力な「はい」である理由、ECSはゲーム業界をはるかに超えて適用できる可能性があることを正確に強調するのに役立つと思います。

1980年代のブルートフォースアーキテクチャ

私がVFX業界で取り組んだ最初のアーキテクチャには、私が入社してから10年以上経った長い遺産がありました。それはブルートフォースの粗いCコーディングでした(私はCが好きなので、Cの傾斜ではありませんが、ここで使用されている方法は本当に粗野でした)。ミニチュアで単純化しすぎたスライスは、次のような依存関係に似ていました。

ここに画像の説明を入力してください

これは、システムのごく一部を非常に単純化した図です。ダイアグラム内のこれらの各クライアント(「レンダリング」、「物理」、「モーション」)は、次のようにタイプフィールドをチェックする「ジェネリック」オブジェクトを取得します。

void transform(struct Object* obj, const float mat[16])
{
    switch (obj->type)
    {
        case camera:
            // cast to camera and do something with camera fields
            break;
        case light:
            // cast to light and do something with light fields
            break;
        ...
    }
}

もちろん、これよりも著しくく、より複雑なコードを使用します。多くの場合、追加の関数がこれらのスイッチケースから呼び出され、繰り返し再帰的にスイッチを実行します。この図とコードはほとんどECS-liteのように見えるかもしれませんが、強いエンティティ成分の区別(「ありませんでしたです、このオブジェクトはカメラ?」、 『このオブジェクトがないではない提供(運動を?』)、および『システム』の無い定式化は、ネストされた関数の束だけが場所を行き来し、責任を混乱させます)。その場合、ほとんどすべてが複雑でしたが、どの機能も災害が起こるのを待っている可能性がありました。

ここでのテスト手順では、他のタイプのアイテムとは別の種類のアイテムとは別のメッシュのようなものをチェックする必要がありました。ここでのコーディングのブルートフォースの性質(多くの場合、コピーと貼り付けが多い)そうでない場合、まったく同じロジックが、あるアイテムタイプから次のアイテムタイプに失敗する可能性が非常に高くなります。システムを拡張して新しいタイプのアイテムを処理しようとしても、既存のタイプのアイテムを処理するだけで苦労するのは非常に困難であったため、ユーザーエンドのニーズが強く表現されていたとしても、かなり絶望的でした。

いくつかの長所:

  • えーと...エンジニアリングの経験は必要ないでしょうね。このシステムは、ポリモーフィズムのような基本的な概念の知識さえも必要とせず、完全に力ずくであるため、デバッグのプロがほとんど維持できない場合でも、初心者でもコードの一部を理解できるかもしれません。

短所:

  • メンテナンスの悪夢。マーケティングチームは、1年のサイクルで2000を超える固有のバグを修正したことを自慢する必要性を実感しました。私にとってそれは、そもそも非常に多くのバグがあったことを恥ずかしく思うことであり、そのプロセスはおそらく、常に増え続けているバグの約10%しか修正しなかったでしょう。
  • 可能な限り最も柔軟性のないソリューションについて。

1990年代のCOMアーキテクチャ

VFX業界のほとんどは、私が収集したものからこのスタイルのアーキテクチャを使用し、設計の決定に関するドキュメントを読み、ソフトウェア開発キットを一glしています。

ABIレベルのCOMではない場合があります(これらのアーキテクチャの一部は、同じコンパイラを使用して記述されたプラグインのみを持つことができます)が、コンポーネントがサポートするインターフェイスを確認するためにオブジェクトに対して行われるインターフェイスクエリと多くの類似した特性を共有します。

ここに画像の説明を入力してください

この種のアプローチでは、transform上記の類推関数はこの形式に似たものになりました。

void transform(Object obj, const Matrix& mat)
{
    // Wrapper that performs an interface query to see if the 
    // object implements the IMotion interface.
    MotionRef motion(obj);

    // If the object supported the IMotion interface:
    if (motion.valid())
    {
        // Transform the item through the IMotion interface.
        motion->transform(mat);
        ...
    }
}

これは、古いコードベースの新しいチームが最終的にリファクタリングするために着手したアプローチです。また、柔軟性と保守性の点で元の製品よりも劇的に改善されましたが、まだ次のセクションでカバーするいくつかの問題がありました。

いくつかの長所:

  • 以前のブルートフォースソリューションよりも劇的に柔軟性/拡張性/保守性が向上します。
  • すべてのインターフェースを完全に抽象化することにより、SOLIDの多くの原則への強い準拠を促進します(ステートレス、実装なし、純粋なインターフェースのみ)。

短所:

  • 定型文がたくさん。オブジェクトをインスタンス化するには、コンポーネントをレジストリを介して公開する必要がありました。サポートするインターフェイスでは、インターフェイスの継承(Javaでの実装)と、クエリで使用可能なインターフェイスを示すコードの提供の両方が必要でした。
  • 純粋なインターフェースの結果として、至る所で複製されたロジックを促進しました。たとえば、実装したすべてのコンポーネントIMotion常にまったく同じ状態とまったく同じ実装を持ちます。これを軽減するために、同じインターフェイスに対して同じ方法で冗長に実装される傾向があるもののためにシステム全体でベースクラスとヘルパー機能を集中化することを開始しました。クライアントのコードは簡単でしたが、ボンネットの下は乱雑です。
  • 非効率性:vtuneセッションでは、QueryInterfaceほとんどの場合、基本機能が中から上部のホットスポットとして表示され、場合によっては#1ホットスポットとしても表示されていました。それを軽減するために、コードベースの一部をレンダリングすることで、既にサポートされていることがわかっているオブジェクトのリストをキャッシュするなどのことを行います。IRenderable、しかしそれは複雑さと保守コストを大幅に増大させました。同様に、これを測定することはより困難でしたが、すべてのインターフェイスが動的ディスパッチを必要とする以前に行っていたCスタイルのコーディングと比較して、明確なスローダウンに気付きました。分岐の予測ミスや最適化の障壁などは、コードの小さな側面以外では測定するのが困難ですが、ユーザーは一般に、ソフトウェアの以前のバージョンと新しいバージョンを並べて比較することにより、ユーザーインターフェイスの応答性や悪化することに気づいていましたアルゴリズムの複雑さは変わらず、定数のみが変更されたエリアに対応します。
  • より広範なシステムレベルでの正確性について推論することは依然として困難でした。以前のアプローチよりもはるかに簡単でしたが、このシステム全体でオブジェクト間の複雑な相互作用を把握することは依然として困難でした。特に、それに対して必要になり始めた最適化の一部についてです。
  • インターフェイスを正しく取得できませんでした。インターフェースを使用するシステム内の広い場所は1つだけかもしれませんが、ユーザーエンドの要件はバージョンによって変わるため、新しい機能を追加するために、インターフェースを実装するすべてのクラスにカスケード変更を行う必要がありますインターフェース。たとえば、内部のロジックをすでに集中化している抽象基底クラスがない限り(これらの一部は、これを何度も繰り返し実行しないことを期待して、これらのカスケード変更の途中で現れます)。

ここに画像の説明を入力してください

実用的な応答:構成

私たちが以前に気づいていた(または少なくとも私が)問題を引き起こしていたことの1つは、IMotion100の異なるクラスによって実装されるが、まったく同じ実装と状態が関連付けられることでした。さらに、レンダリング、キーフレームモーション、物理学などの少数のシステムでのみ使用されます。

そのため、このような場合、インターフェースへのインターフェースを使用するシステム間の3対1の関係、およびインターフェースへのインターフェースを実装するサブタイプ間の100対1の関係があります。

複雑さとメンテナンスは、に依存する3つのクライアントシステムではなく、100のサブタイプの実装とメンテナンスに大きく偏りますIMotion。これにより、メンテナンスの難しさはすべて、インターフェイスを使用する3つの場所ではなく、これらの100のサブタイプのメンテナンスにシフトしました。「間接的な遠心性カップリング」がほとんどまたはまったくない(直接的な依存性ではなく、インターフェースを介した間接的な)コード内の3つの場所を更新します。大したことはありません。 、かなり大した*。

* この意味で、実装の観点から「遠心性カップリング」の定義をねじ込むのは奇妙で間違っていることを認識しています。変更する必要があります。

だから私は一生懸命にプッシュしなければなりませんでしたが、もう少し実用的になり、「純粋なインターフェース」のアイデア全体を緩和することを提案しました。IMotion豊富な種類の実装を使用するメリットがない限り、完全に抽象的でステートレスなものを作成しても意味がありません。私たちの場合、IMotionさまざまな実装を行うことは、実際にはメンテナンスの悪夢になります。多様性は望まなかったからです。その代わりに、クライアントの要件の変化に対して本当に優れた単一のモーション実装を試みることを目指して反復し、多くの場合、純粋なインターフェースのアイデアを回避して、すべての実装者IMotionに同じ実装と関連付けられた状態を使用させ、 t目標を複製します。

したがって、インターフェースBehaviorsは、エンティティに関連付けられた広範なものになりました。IMotion単にMotion「コンポーネント」になります(「コンポーネント」を定義する方法を、COMから、「完全な」エンティティを構成する通常の定義に近いものに変更しました)。

これの代わりに:

class IMotion
{
public:
    virtual ~IMotion() {}
    virtual void transform(const Matrix& mat) = 0;
    ...
};

次のようなものに進化させました。

class Motion
{
public:
    void transform(const Matrix& mat)
    {
        ...
    }
    ...

private:
    Matrix transformation;
    ...
};

これは、依存関係の反転の原則に対する露骨な違反であり、抽象から具体へと移行し始めますが、このような抽象化のレベルは、合理的な疑いを超えて将来の真のニーズを予測できる場合にのみ有用ですそのような柔軟性のために、ユーザーエクスペリエンスから完全に切り離されたとんでもない「what if」シナリオを実行する(おそらくデザインの変更が必要になるでしょう)。

そこで、私たちはこの設計へと進化し始めました。QueryInterfaceのようになったQueryBehavior。さらに、ここで継承を使用することは無意味に見え始めました。代わりに構成を使用しました。オブジェクトは、実行時に可用性を照会して注入できるコンポーネントのコレクションになりました。

ここに画像の説明を入力してください

いくつかの長所:

  • 私たちの場合、以前の純粋なインターフェースのCOMスタイルのシステムよりも、保守がずっと簡単でした。要件の変更やワークフローの苦情などの予期せぬ驚きは、1つの非常に中心的で明白なMotion実装(たとえば、100のサブタイプに分散していない)により簡単に対応できます。
  • 実際に必要な、まったく新しいレベルの柔軟性を提供しました。以前のシステムでは、継承が静的な関係をモデル化するため、C ++でのコンパイル時にのみ新しいエンティティを効果的に定義できました。スクリプト言語からはできませんでした。たとえば、構成アプローチでは、コンポーネントをリストに追加して追加するだけで、実行時にその場で新しいエンティティをまとめることができました。「エンティティ」は空のキャンバスになり、その上で必要なもののコラージュをその場ですぐに投げることができ、関連するシステムは結果としてこれらのエンティティを自動的に認識して処理します。

短所:

  • 私たちはまだ効率部門で苦労し、パフォーマンスが重要な分野で保守性を維持していました。各システムは、これらの動作を繰り返し提供し、利用可能なものをチェックすることを回避するために、これらの動作を提供するエンティティのコンポーネントをキャッシュしたいままになります。パフォーマンスを要求する各システムはこれをわずかに異なる方法で行い、このキャッシュされたリストと場合によってはデータ構造(フラスタムカリングまたはレイトレーシングなどの検索の形式が含まれる場合)の更新に失敗するさまざまなバグが発生する傾向がありました不明瞭なシーン変更イベント、例えば
  • これらのきめ細かな行動のシンプルなオブジェクトすべてに関連して、指を置くことができなかった厄介で複雑なものがまだありました。これらの「動作」オブジェクト間の相互作用に対処するために、時々必要な多くのイベントを生成し、結果は非常に分散化されたコードになりました。それぞれの小さなオブジェクトは、正確さをテストするのが簡単で、個別に撮影すると、しばしば完全に正しいものでした。それでも、小さな村々で構成される大規模な生態系を維持しようとしているように感じ、それらすべてが個々に何をして全体として作り上げているのかを推論しようとしています。Cスタイルの80年代のコードベースは、1つの壮大な過密メガロポリスのように感じられましたが、これは間違いなくメンテナンスの悪夢でしたが、
  • 抽象性の欠如による柔軟性の喪失。しかし、実際にそれに対する真の必要性に実際に出会ったことのない領域では、実用的な欠点はほとんどありません(少なくとも理論的な欠点はありますが)。
  • ABIの互換性を維持することは常に困難でした。これにより、「動作」に関連付けられた安定したインターフェイスだけでなく、安定したデータが必要になるため、困難になりました。ただし、状態の変更が必要な場合は、新しい動作を簡単に追加し、既存の動作を単純に廃止できます。これは、バージョン管理の問題を処理するためにサブタイプレベルのインターフェイスの下でバックフリップを行うよりも間違いなく簡単です。

発生した現象の1つは、これらの動作コンポーネントの抽象化を失ったため、より多くのコンポーネントがあったことです。たとえば、抽象IRenderableコンポーネントの代わりに、コンクリートMeshまたはPointSpritesコンポーネントでオブジェクトをアタッチします。レンダリングシステムは、レンダリング方法MeshPointSpritesコンポーネントを認識し、そのようなコンポーネントを提供し、それらを描画するエンティティを見つけます。他の時にはSceneLabel、後知恵で必要だとわかったようなさまざまなレンダリング可能なものがあったのでSceneLabel、それらのケースでは関連エンティティに(おそらくに加えてMesh)を添付しました。レンダリングシステムの実装は、それらを提供するエンティティをレンダリングする方法を知るために更新され、それは非常に簡単な変更でした。

この場合、コンポーネントで構成されるエンティティを別のエンティティのコンポーネントとして使用することもできます。レゴブロックを接続して、そのように構築します。

ECS:システムおよび未加工データコンポーネント

その最後のシステムは私が自分で作った限りであり、私たちはまだCOMでそれを酷使していました。エンティティー・コンポーネント・システムになりたいと思っていましたが、当時は私はそれをよく知りませんでした。建築のインスピレーションのためにAAAゲームエンジンを検討すべきだったときに、私は自分の分野を飽和させたCOMスタイルの例を見て回っていました。私はついにそれを始めました。

欠けていたのは、いくつかの重要なアイデアでした。

  1. 「コンポーネント」を処理する「システム」の形式化。
  2. 「コンポーネント」は、大きなオブジェクトにまとめられた動作オブジェクトではなく、生データです。
  3. コンポーネントのコレクションに関連付けられた厳密なIDにすぎないエンティティ。

私はついにその会社を辞め、インディとしてECSの作業を開始し(貯蓄を使い果たしながら作業を続けています)、これは管理が最も簡単なシステムでした。

ECSアプローチで気付いたのは、上記でまだ苦労していた問題を解決したことです。私にとって最も重要なことは、複雑な相互作用のある小さな村ではなく、健康的なサイズの「都市」を管理しているように感じたということです。モノリシックな「メガロポリス」ほど維持するのは難しくなく、人口が多すぎて効果的に管理することはできませんでしたが、それらの間で悪夢のようなグラフを形成しました。ECSはすべての複雑さを、レンダリングシステム、健全なサイズの「都市」のような「過密なメガロポリス」ではなく、かさばる「システム」に向けて蒸留しました。

生データになるコンポーネントは、OOPの基本的な情報隠蔽の原則さえも破るので、最初は本当に奇妙に感じました。OOPについて私が大切にしていた最大の価値の1つに挑戦することでした。しかし、インターフェイスの組み合わせを実装する数百から数千のサブタイプにそのようなロジックを分散させるのではなく、そのデータを変換するダースまたは非常に広範なシステムで何が起こっているのかがすぐに明らかになったため、それは無関心になり始めました。システムがデータにアクセスする機能と実装を提供し、コンポーネントがデータを提供し、エンティティがコンポーネントを提供している場合を除き、OOPスタイルのスタイルであると考える傾向があります。

広いパスでデータを変換するかさばるシステムがほんの一握りしかない場合に、システムによって引き起こされる副作用について推論するのは、直感に反してさらに簡単になりました。システムはかなり「フラット」になり、各スレッドの呼び出しスタックはこれまでになく浅くなりました。その監督レベルのシステムについて考えることができ、奇妙な驚きにぶつかることはありませんでした。

同様に、これらのクエリを削除することに関して、パフォーマンスが重要な領域でさえも簡単にしました。「システム」の概念が非常に形式化されたため、システムは関心のあるコンポーネントをサブスクライブし、その基準を満たすエンティティのキ​​ャッシュされたリストを渡すことができました。各キャッシングの最適化を管理する必要はなく、単一の場所に集中化されました。

いくつかの長所:

  • 予期せぬニーズに遭遇したときにデザインコーナーに閉じ込められたことを感じることなく、私のキャリアで遭遇したほぼすべての主要な建築上の問題を解決するようです。

短所:

  • 私は時々頭を悩ませるのに苦労しています。ゲーム業界内でも、それが何を意味するのか、どうすればいいのかについて議論する最も成熟した、あるいは確立されたパラダイムではありません。COMスタイルの考え方や元のコードベースの1980年代のCスタイルの考え方に深く関わっているメンバーで構成された元チームでできたことは間違いありません。私が時々混乱するのは、コンポーネント間のグラフスタイルの関係をモデル化する方法のようなものですが、後で別のコンポーネントに依存するコンポーネントを作ることができる恐ろしいことが判明しなかったソリューションを常に見つけましたコンポーネントは親としてこの他のコンポーネントに依存し、システムはメモ化を使用して、同じ再帰的モーション計算を繰り返し行うことを避けます。
  • ABIは依然として困難ですが、これまでのところ、純粋なインターフェイスアプローチよりも簡単だと言ってさえ思います。それは考え方の変化です:データの安定性は、インターフェースの安定性よりもABIの唯一の焦点になり、ある意味では、インターフェースの安定性よりもデータの安定性を達成する方が簡単です(例:新しいパラメーターが必要なために関数を変更する誘惑はありません)。そのようなことは、ABIを壊さない粗いシステム実装内で行われます。

ここに画像の説明を入力してください

ただし、ゲームエンジンで一般的なComponent-Entity-Systemアーキテクチャを使用してアプリケーションを作成することは合理的ですか?

とにかく、私は絶対に「はい」と言い、私の個人的なVFXの例は有力候補です。しかし、それはまだゲームのニーズにかなり似ています。

ゲームエンジンの懸念から完全に切り離された遠隔地での実践は行っていませんが(VFXは非常に似ています)、ECSアプローチの候補としてはるかに多くの分野があるように思えます。おそらくGUIシステムでさえも適切でしょうが、私はそこでさらにOOPアプローチを使用します(ただし、Qtとは異なり、深い継承はありません)。

それは広く未開拓の領域ですが、あなたのエンティティが「特性」の豊富な組み合わせ(およびそれらが提供する特性のコンボが常に変化する可能性がある)で構成できる場合、そしてあなたが少数の一般化されている場合必要な特性を持つエンティティを処理するシステム。

これらのケースでは、多重継承または概念のエミュレーション(ミックスインなど)を使用して、深い継承階層または数百のコンボで数百以上のコンボを生成したい場合のシナリオの非常に実用的な代替手段になります。インターフェイスの特定のコンボを実装するフラットな階層のクラスですが、システムの数が少ない場合(数十など)。

これらの場合、コードベースの複雑さは、タイプの組み合わせの数ではなく、システムの数に比例するように感じ始めます。これは、各タイプが、生データにすぎないコンポーネントを構成する単なるエンティティになったためです。GUIシステムは当然、これらの種類の仕様に適合し、他の基本タイプまたはインターフェースから結合された数百の可能なウィジェットタイプがありますが、それらを処理する少数のシステム(レイアウトシステム、レンダリングシステムなど)のみがあります。GUIシステムがECSを使用している場合、継承されたインターフェースまたは基本クラスを持つ数百の異なるオブジェクトタイプの代わりに、これらのシステムのすべてによってすべての機能が提供されると、システムの正確性について推論するのがおそらくはるかに簡単になります。GUIシステムがECSを使用した場合、ウィジェットには機能がなく、データのみがあります。機能を持つのは、ウィジェットエンティティを処理するほんの一握りのシステムだけです。ウィジェットのオーバーライド可能なイベントがどのように処理されるかは私を超えていますが、これまでの限られた経験に基づいて、そのタイプのロジックを特定のシステムに集中的に転送することができない場合は、後知恵は、私が今まで期待していたはるかにエレガントなソリューションをもたらしました。

私の命の恩人だったので、私はそれがより多くの分野で使われるのを見てみたいです。もちろん、コンポーネントを集約するエンティティからそれらのコンポーネントを処理する粗いシステムまで、設計がこのように分解しない場合は不適切ですが、自然にこの種のモデルに適合する場合、これは私がこれまでに遭遇した中で最も素晴らしいものです。


1)VFXプログラムの例は、ユーザーの観点から何をしましたか?2)現在、どのECSプロジェクトに取り組んでいますか?♥これを書いてくれてありがとう!♥
子犬

1
非常に徹底した説明-ありがとう。ECSがゲームを超えてどのように適用できるかに関して、あなたと同じ結論の多くに到達しているように感じます。私の場合、特に複雑なGUIです。最初は、通常行われていること(UIフレームワークでは特に深い継承階層が特に顕著です)に反することは、最初は本当に奇妙に感じますが、このアプローチをより効果的にしている他の人を見るのは心強いものです。
ダニーヤロ

1
このすばらしい...記事をありがとう!コンポーネントベースのGUIについては、Unity3dのUGUIを確認することをお勧めします。CocoaTouchのような継承ベースのものに比べて、非常に柔軟で拡張可能です。
イヴァンミール

16

ゲームエンジンのコンポーネントエンティティシステムアーキテクチャは、ゲームソフトウェアの性質、およびその独自の特性と品質要件のために、ゲームで機能します。たとえば、エンティティは、ゲーム内の物事に対処して作業するための統一された手段を提供します。これは、目的と用途が大幅に異なる場合がありますが、システムによって統一された方法でレンダリング、更新、またはシリアライズ/デシリアライズする必要があります。このアーキテクチャにコンポーネントモデルを組み込むことにより、シンプルなコア構造を維持しながら、必要に応じてより少ないコードカップリングで機能を追加できます。CADアプリケーション、A / Vコーデックなど、この設計の特徴を活用できるさまざまなソフトウェアシステムがあります。

TL; DR-設計パターンは、問題の領域が設計に課す機能と欠点に十分に適している場合にのみ機能します。


8

問題のドメインがそれに適している場合は、確かに。

私の現在の仕事には、実行時の要因に応じてさまざまな機能をサポートする必要があるアプリが含まれます。コンポーネントベースのエンティティを使用してこれらの機能をすべて分離し、拡張性とテスト容易性を分離して実現することは、私たちにとって牧歌的です。

編集: 私の仕事には、専用ハードウェア(C#)への接続性の提供が含まれます。ハードウェアのフォームファクター、インストールされているファームウェア、クライアントが購入したサービスのレベルなどに応じて、デバイスにさまざまなレベルの機能を提供する必要があります。同じインターフェースを持つ一部の機能でも、デバイスのバージョンに応じて実装が異なります。

ここにある以前のコードベースには、非常に幅広いインターフェイスがあり、多くは実装されていません。いくつかは、多くのシンインターフェースを持っていて、1つのクラスで静的に構成されていました。単に文字列->文字列辞書を使用してモデル化したものもあります。(私たちには、もっとうまくやれると思う部門がたくさんあります)

これらにはすべて欠点があります。幅の広いインターフェイスは、効果的にモック/テストするのに苦労します。新しい機能を追加するということは、パブリックインターフェイス(およびすべての既存の実装)を変更することを意味します。多くのシンインターフェースは非常にいコードを消費しましたが、最終的には大きな脂肪オブジェクトをテストすることに苦労しました。さらに、シンインターフェイスは依存関係を適切に管理しませんでした。文字列辞書には、通常の解析と存在の問題、およびパフォーマンス、可読性、保守性の問題があります。

現在使用しているのは、ランタイム情報に基づいてコンポーネントを検出および構成する非常にスリムなエンティティです。依存関係は宣言的に行われ、コアコンポーネントフレームワークによって自動解決されます。コンポーネント自体は、依存関係を直接処理するため、単独でテストできます。依存関係の欠落の問題は、依存関係の最初の使用ではなく、1つの場所で早期に発見されます。新しい(またはテスト)コンポーネントをドロップインすることができ、既存のコードが影響を受けることはありません。コンシューマーはエンティティにコンポーネントへのインターフェースを要求するため、比較的自由にさまざまな実装(および実装がランタイムデータにどのようにマップされるか)を自由に調整できます。

このような状況で、オブジェクトとそのインターフェースの構成に共通コンポーネントの(非常に多様な)サブセットを含めることができる場合、非常にうまく機能します。


1
許可されていると仮定して、現在の作業の詳細を提供できますか?あなたが構築しているものに対してCESがどのように牧歌的であったかを知りたいです。
アンドリューデアンドラーデ

あなたの経験に関する記事、論文、ブログはありますか?また、私はそれについてもっと技術的な詳細が
欲しいです

@ user1778770-非公開、いいえ。どんな質問がありましたか?
テラスティン14

さて、簡単なことから始めましょう。あなたのコンセプトはアプリケーションスタック全体(ビジネスからフロントエンドまで)に及びますか?または単一のユースケースの単一のレイヤーのみ?
user1778770 14

@ user1778770-私の実装では、エンティティ/コンポーネントは1つのレイヤーに存在します。異なるエンティティは異なるレイヤーに存在する場合がありますが、多くの場合、1:1ではありません(またはレイヤーがメリットを提供しない場合)。
テラスティン14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.