エンティティコンポーネントシステムで「ブロブシステム」を回避する方法


10

現在、私は次の問題に直面しています:

エンティティコンポーネントシステム(ECS)を使用してポンクローンを作成しようとしています。「フレームワーク」はすべて自分で書きました。したがって、すべてのコンポーネントでエンティティを管理するクラスがあります。次に、コンポーネントクラス自体があります。そして最後に、システムが必要とするコンポーネントを持つすべてのエンティティを取得する私のシステムがあります。

たとえば、私の移動システムは、位置コンポーネントと移動コンポーネントを持つすべてのエンティティを探します。位置コンポーネントは位置を保持し、移動コンポーネントは速度を保持します。

しかし、実際の問題は私の衝突システムです。このクラスは論理ブロブのようなものです。このクラスには特別なケースがたくさんあります。

例:パドルがボーダーと衝突する可能性があります。この場合、速度はゼロに設定されます。私のボールも国境にぶつかることがあります。しかし、この場合、その速度は境界線の法線でミラーリングされるだけなので、反映されます。これを行うために、私はボールに追加の物理コンポーネントを与えました。したがって、実際には、物理​​コンポーネントには実際のデータはありません。これは、オブジェクトが反射または停止したかどうかをシステムに通知するために存在する空のクラスです。

次に、これが発生します。ボールがパドルまたは境界に衝突したときに、いくつかのパーティクルをレンダリングしたいと思います。したがって、ボールは、衝突時にパーティクルを作成するように衝突システムに指示する別のコンポーネントを取得する必要があると思います。
次に、パドルと衝突するが境界とは衝突しないパワーアップが必要です。それが起こった場合、パワーアップは消えなければなりません。したがって、さらに多くのケースとコンポーネントが必要になります(一部のエンティティは特定のエンティティとのみ衝突できることをシステムに伝えるために、他の一部が実際に衝突できるとしても、すべてではなくボット、さらに衝突システムはパワーアップを適用する必要がありましたパドルなどなど)。

エンティティコンポーネントシステムは柔軟性があり、継承に問題がないので、良いことだと思います。しかし、私は現在完全に行き詰まっています。

複雑すぎると思いますか?この問題にどのように対処すればよいですか?

もちろん、私は実際に「衝突後」の原因となるシステムを作成する必要があります。そのため、衝突システムは「はい、最後のフレームに衝突があります」とだけ表示し、次に「衝突後」システムがたくさんあります。すべて異なるコンポーネント(の組み合わせ)を必要とし、コンポーネントを変更します。たとえば、衝突が発生したときに停止する必要があるものを停止する衝突後移動システムがあります。次に、物事を反映する物理衝突後システムなど。

しかし、これは私にとっても適切な解決策ではないようです。たとえば、

  1. 私の移動後衝突システムには、位置コンポーネント、移動コンポーネント、および衝突コンポーネントを持つエンティティが必要です。次に、エンティティの速度をゼロに設定します。
  2. 物理衝突後システムには、位置コンポーネント、移動コンポーネント、衝突コンポーネント、および物理コンポーネントを持つエンティティが必要です。次に、速度ベクトルを反映します。

問題は明白です。衝突後の動きには、物理​​衝突後システムのエンティティのサブセットであるエンティティが必要です。したがって、2つの衝突後システムは同じデータで動作し、その効果は次のとおりです。エンティティには物理コンポーネントがありますが、衝突後の速度はゼロです。

これらの問題は、エンティティコンポーネントシステムで一般的にどのように解決されますか?それらの問題はいつものことですか?それとも私は何か間違っていますか?はいの場合、代わりに何をどのように行う必要がありますか?

回答:


11

はい、あなたは複雑すぎると考えています。

問題の多くは、メッセージングシステムと、いくつかのフィルターを指定できるいくつかの追加の属性で解決でき、最後にエンティティ/コンポーネントがそれほど厳格であるかどうかを心配する必要がないようです。

メッセージングは​​、パーティクルの起動、パワーアップなどのいくつかの側面で役立ちます。たとえば、パーティクルイベントをサブスクライブし、イベントで記述された位置にパーティクルを作成するワールドオブジェクトを作成できます。

フィルターは衝突の際に役立ちます。フィルターは、オブジェクトが別のオブジェクトと衝突するかどうか、およびオブジェクトがどのような応答をするかを定義できます。物理コンポーネントにいくつかの属性を追加して、それがどのタイプの物理ボディであるか、他のどのタイプの物理ボディと衝突するか、どのような応答になるかを定義します。たとえば、ボールの物理オブジェクトはパドルの物理オブジェクトと衝突し、反射と粒子で応答します。

最後に、実装についてそれほど厳しくしないでください。それを機能させる方法を見つけることができても、それが実際にはECシステムではない場合は、実行してください。上記の私の例のように、パーティクルはシステムまたはECシステムの一部によってまったく管理される必要はありません。ゲームを完成させることの方が、すでにかなり明確に定義されていない方法に厳密に従うことよりも重要です。


どうもありがとうございました。ECSを使用してゲームを構築したいのは、それがどのようにスケールするかを確認するためであり、記事やチュートリアルで読むときに本当にそれが本当に使いやすいかどうかです。私の問題は、「今ECSがあり、すべてをこれで管理する必要がある」と思ったことです。そのため、ECSに関連してパーティクルシステムを作成することも計画しました。また、私はいくつかの記事を読みましたが、すべてのコンポーネントには実際には基本的なデータのみがあり、それ以上のものはないはずです。それはしばしば私の問題です...複雑すぎると思います。
M0rgenstern 2013年

ECSを使用してゲームを構築したいのは、それがどのようにスケールするかを確認するためであり、記事やチュートリアルで読んだときに本当に使いやすいかどうかです。それがあなたの目標であるなら、私はあなたがあなた自身のものを構築する代わりに既存のコンポーネント/エンティティシステムを見ることを勧めます。Unity3Dをダウンロードしてください。これはおそらく「入手可能な限り純粋なコンポーネント」であり、そこで遊んでみてください。はるかに速い洞察力、私見。
Imi

3
@lmi:Unityはコンポーネントベースですが、エンティティコンポーネントシステムではありません。ECSには、ゲームオブジェクトコンポーネントを単に使用して使用するよりも、厳密なガイドラインがあります(パターンをルールとして考えないでください)。一連の記事により、ECSは現在ゲーム開発者の一部のセグメントで人気があるため、一般的にコンポーネントベースのデザインではなく、具体的にはECSに関する多くの質問があります。
Sean Middleditch 2013年

12

あなたは物事を複雑にしています。コンポーネントベースのデザインを使用することさえ、そのような単純なゲームにとってはやり過ぎであるとまで言えます。ゲームをすばやく簡単に開発できるようにしてください。コンポーネントは、多種多様な動作とゲームオブジェクト構成を備えた大規模プロジェクトでの反復に役立ちますが、そのような単純で明確に定義されたゲームへの利点はより疑わしいものです。去年私はこれについて話しましたアーキテクチャ固執するのではなくゲーム作ることに集中すれば、数時間で楽しい小さなゲームを構築できます。継承は、100個または20個の異なるタイプのオブジェクトがある場合に機能しなくなりますが、少数のオブジェクトしか存在しない場合は問題なく機能します。

学習目的でコンポーネントを使い続けたいとすると、アプローチには明らかな問題がいくつかあります。

まず、コンポーネントをそれほど小さくしないでください。「動き」のようなきめの細かいコンポーネントを使用する理由はありません。あなたのゲームには一般的な動きはありません。パドルがあり、その動きは入力またはAIに密接に関連付けられており(速度、加速度、反発力などは実際には使用しません)、明確に定義された動きアルゴリズムを持つボールがあります。PaddleControllerコンポーネントとBouncingBallコンポーネント、またはそれらのラインに沿った何かを持っているだけです。より複雑なゲームを入手した場合、より一般的なPhysicsBodyコンポーネント(「実際の」エンジンでは、基本的にはゲームオブジェクトとHavok / PhysX / Bullet / Box2D / etc。)は、さまざまな状況に対応します。

「位置」コンポーネントでさえ疑わしいですが、珍しいことではありません。物理エンジンは通常、オブジェクトの場所に関する独自の内部概念を持っています。グラフィックには補間表現があり、AIには異なる状態の同じデータのさらに別の表現がある場合があります。各システムに、システム自体のコンポーネントでの変換の独自のアイデアを管理させ、システム間の円滑な通信を確保することを許可することは、有利な場合があります。イベントストリームに関するBitSquidブログ投稿を参照してください。

カスタム物理エンジンの場合、コンポーネントに関するデータを保持することが許可されていることを覚えておいてください。たぶん、一般的なPong物理コンポーネントには、どの軸に移動できるかを示すデータがあります(たとえば、vec2(0,1)Y軸上でのみ移動できるパドルとvec2(1,1)、移動できることを示すボールの乗数として)、弾力性を示すフラグまたはフロート(ボールは通常、に1.0あり、パドルは0.0)、加速特性、速度など。これを、関連性の高いデータの断片ごとに数十億の異なるマイクロコンポーネントに分割しようとすることは、ECSの本来の目的に反しています。一緒に使用されるものは同じコンポーネントで可能な限り保持し、各ゲームオブジェクトがそのデータを使用する方法に大きな違いがある場合にのみそれらを分割します。Pongの場合、ボールとパドルの間の物理的性質が別々のコンポーネントになるほど十分に異なるという議論がありますが、より大きなゲームでは、1〜3でうまく機能するように20のコンポーネントを作成しようとする理由はほとんどありません。

ECSのバージョンが邪魔になる場合は、実際にゲームを作成するために必要なことを行い、設計パターン/アーキテクチャへの厳格な遵守を忘れてください。


1
本当にありがとう!私は、ECSがピンポンのような小さなゲームにうまく対応できないことを知っています。しかし、私はそのようなものが実際にどのように実装され、どのように機能するかを確認するためだけにそれを使用しました。私がいくつかの記事で主に読んだので、コンポーネントをとても小さくしました。多くのコンポーネントを持ち、各コンポーネントは基本データのみを保持します。それで、私はあなたが正しいと理解していますか?あなたは継承とECSの混合を使用することを勧めますか?あなたが言うように、「ボールとパドルは別々のコンポーネントになるほど十分に異なっています」。そのため、たとえば、
Move

1
機能するものは何でも。ゲームの作成に集中してください。文字通りBall、動き、跳ね返りなどのボールのすべてのロジックを含むと呼ばれるコンポーネントと、Paddle入力を受け取るコンポーネントがありますが、それは私です。あなたにとって最も理にかなっていて邪魔にならないもの、ゲームを作ることができるものは何でも「正しい方法」です。
Sean Middleditch 2013年

3
文字通り、Ballと呼ばれる、動きや跳ね返りなどのボールのすべてのロジックを含むコンポーネントと、入力を受け取るPaddleコンポーネントがありますが、それは私です。そして、それが「コンポーネントシステムとは何か」について、プログラマごとに1つの意見がある理由です。私はお勧めしませあなたは完全に古典的なエンティティのシステムで考えているとコンポーネントシステムを使用するように強制されている以外、この提案のようにそれを行うにはその差は実際にあるものを見たくありません。
Imi

2
@lmi:コンポーネントを使用していくつかの大きなゲーム/エンジンに取り組み、コンポーネントを使用する理由を直接見た場合、いいえ、過度に細かいコンポーネントは価値があるよりも問題が多いだけです。コンポーネントは特効薬ではありません。これらは、ゲーム開発者のツールボックスにある多くのツールの1つです。システムに精神的および実行時のオーバーヘッドを追加するだけではなく、役立つ方法と場所でそれらを使用してください。ボールの物理学を持っている唯一のものがボールであれば、そこにあるゼロの他のボール特性からそれを分離する利点は。それが変更された場合は、それを分割し、次に分割します。
Sean Middleditch 2013年

1
私は実用的であり、特定のパターンを邪魔させないという原則に同意します。しかし、ECSが逸脱することなくこの簡単なゲームを処理できない場合、大規模なゲームにはどのような希望がありますか。私もECSを効果的に使用する方法を学習しようとしていますが、できるだけECSの哲学に近づこうとしています。
Ken

-2

コンポーネントのためにここにされていません。私の意見*)では、コンポーネントを使用して最大の問題はこれです教えて何をすべきか、誰を。コンポーネントは、物事を行うためにここにあります。何かのメモリを保持するだけのコンポーネントはなく、他のコンポーネントでこれを操作します。取得したデータを処理するコンポーネントが必要です。

他のコンポーネントの存在をテストしている(そしてそこで関数を呼び出している)場合、これは明確な兆候であり、次の2つのいずれかが当てはまります。

  • 実際に依存関係を反転したい場合:他のコンポーネントは、イベント/メッセージ/ブロードキャスト/フック/名前をリッスンして、現在のコンポーネントに応じてロジックを実行する必要があります。現在のコンポーネントは、「他の」コンポーネントがあることを知る必要さえありません。これは、現在のメソッドに実際には接続されていない機能を備えたさまざまなコンポーネント(else / caseブロックが異なる場合でも)を呼び出す場合によく発生します。これらすべてを考えるInvalidate()SetDirty()、他のコンポーネントへの呼び出しです。
  • コンポーネントが多すぎる可能性があります。2つのコンポーネントが互いに存在し合うことができず、常にデータを取得して互いにメソッドを呼び出す必要がある場合は、それらをマージするだけです。明らかに、それらが提供する機能は非常に絡み合っているため、実際には1つだけです。

ちなみに、これらはすべての種類のシステムに適用されます。エンティティ/コンポーネントシステムだけでなく、単純な "GameObject"またはライブラリ関数を使用した従来の継承にも適用されます。

*)本当にだけですWhats Da Real Component Systemz(TM)についての意見は大きく異なります

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.