ゲームコンポーネント、ゲームマネージャー、オブジェクトプロパティ


15

私は、コンポーネントベースのエンティティ設計に頭を悩ませようとしています。

私の最初のステップは、オブジェクトに追加できるさまざまなコンポーネントを作成することでした。すべてのコンポーネントタイプに対して、iにはマネージャーがあり、マネージャーはすべてのコンポーネントの更新機能を呼び出し、必要に応じてキーボードの状態などを渡します。

次にしたことは、オブジェクトを削除し、各コンポーネントにIDを持たせることでした。したがって、オブジェクトは同じIDを持つコンポーネントによって定義されます。

今、私はすべてのコンポーネントのマネージャーは必要ないと考えています。たとえばSizeComponentSizeプロパティを持っているだけです。結果としてSizeComponent更新メソッドはなく、マネージャーの更新メソッドは何もしません。

私の最初の考えはObjectProperty、コンポーネントをコンポーネントのプロパティとして持つのではなく、コンポーネントがクエリできるクラスを持つことでした。だから、オブジェクトが多数を持っているでしょうObjectPropertyし、ObjectComponentます。コンポーネントには、オブジェクトのプロパティを照会する更新ロジックがあります。マネージャーは、コンポーネントの更新メソッドの呼び出しを管理します。

これはオーバーエンジニアリングのように思えますが、マネージャーがどのオブジェクトにどのコンポーネントロジックを実行する必要があるかを知る方法が必要なので、コンポーネントを取り除くことができるとは思いません(そうでなければコンポーネントを削除するだけです更新ロジックを完全にマネージャーにプッシュします)。

  1. この(持っているObjectPropertyObjectComponentComponentManagerオーバーエンジニアリングクラス)?
  2. 良い選択肢は何でしょうか?

1
コンポーネントモデルを学習することで正しいアイデアを得ることができますが、何をする必要があるのか​​をよりよく理解する必要があります。そのための唯一の方法は、ゲームを使用せずに[ほとんど]完了することです。aの作成SizeComponentはやり過ぎだと思います-ほとんどのオブジェクトにはサイズがあると仮定できます-コンポーネントモデルが使用されるのは、レンダリング、AI、物理学などです。サイズは常に同じように動作するため、そのコードを共有できます。
ジョナサンディキンソン


@ JonathanDickinson、@ Den:私の考える問題は、共通のプロパティをどこに保存するかです。たとえば、a RenderingComponentおよびa によって使用される位置としてのオブジェクトPhysicsComponent。資産をどこに置くかについての判断を過剰に考えていますか?どちらかに固定するだけで、他のクエリに必要なプロパティを持つコンポーネントのオブジェクトを照会させる必要がありますか?
ジョージダケット

私の以前のコメント、およびその背後にある思考プロセスは、コンポーネントがクエリできるプロパティ(または関連するプロパティのグループ)の個別のクラスを持つように私を駆り立てるものです。
ジョージダケット

1
私はそのアイデアが本当に好きです。試してみる価値があるかもしれません。しかし、個々のプロパティを記述するオブジェクトを持つことは本当に高価です。PhysicalStateInstance(オブジェクトごとに1つ)とGravityPhysicsShared(ゲームごとに1つ)を試すことができます。しかし、これは建築家の多幸感の領域に進出していると言いたがります。自分自身を穴に建築しないでください(最初のコンポーネントシステムで行ったとおりです)。接吻。
ジョナサンディキンソン

回答:


6

最初の質問に対する簡単な答えは、はい、あなたは設計を過剰に設計しています。「どこまで物事を分解するのですか?」次のステップが実行され、中央オブジェクト(通常はエンティティと呼ばれる)が削除されるとき、質問は非常に一般的です。

オブジェクトをそれ自体のサイズを持つような詳細なレベルに分解すると、デザインが行き過ぎました。データ値自体はコンポーネントではありません。これは組み込みのデータ型であり、多くの場合、呼び出しを開始したとおりのプロパティと呼ばれます。プロパティはコンポーネントではありませんが、コンポーネントにはプロパティが含まれています。

そのため、コンポーネントシステムで開発する際に私が試み、従ういくつかのガイドラインを次に示します。

  • スプーンはありません。
    • これは、中央のオブジェクトを取り除くためにすでに行った手順です。これにより、Entityオブジェクトに何が入るのか、コンポーネントに何が入るのかという議論全体がなくなります。
  • コンポーネントは構造ではありません
    • データを含む場所に何かを分解すると、それはもはやコンポーネントではなく、単なるデータ構造になります。
    • コンポーネントには、特定の方法で非常に特定のタスクを実行するために必要なすべての機能が含まれている必要があります。
    • IRenderableインターフェースは、ゲーム内のあらゆるものを視覚的に表示する汎用ソリューションを提供します。CRenderableSpriteおよびCRenderableModelは、それぞれ2Dおよび3Dでレンダリングするための詳細を提供するインターフェースのコンポーネント実装です。
    • IUseableは、プレーヤーが操作できるもののインターフェイスです。CUseableItemは、アクティブな銃を発射するか、選択したポーションを飲むコンポーネントです。一方、CUseableTriggerは、プレイヤーが砲塔に飛び込んだり、跳ね橋を落とすためにレバーを投げたりする場所です。

そのため、コンポーネントが構造ではないというガイドラインにより、SizeComponentの分解が進みすぎています。データのみが含まれており、何かのサイズを定義するものはさまざまです。たとえば、レンダリングコンポーネントでは、1dスカラーまたは2 / 3dベクトルを使用できます。物理コンポーネントでは、オブジェクトの境界ボリュームである可能性があります。インベントリアイテムでは、2Dグリッド上でどれだけのスペースを占有する可能性があります。

理論と実用性の間に良い線を引いてみてください。

お役に立てれば。


一部のプラットフォームでは、インターフェイスから関数を呼び出すことは親クラスから呼び出すよりも長いことを忘れないでください(答えにはインターフェイスとクラスの言及が含まれていたため)
ADB

覚えておくのが良い点ですが、私は言語にとらわれず、一般的なデザイン用語でそれらを使用することを試みていました。
ジェームズ

「データが含まれている場所に何かを分割すると、それはもはやコンポーネントではなく、単なるデータ構造になります。」 - なぜ?「コンポーネント」は、データ構造も意味するような一般的な単語です。
ポールマンタ

@PaulMantaはい、それは一般的な用語ですが、この質問と回答の要点はどこに線を引くかです。あなたが引用したように、私の答えは、ちょうどそれを行うための経験則に対する私の提案です。いつものように、理論や設計上の考慮事項を開発の原動力にすることは絶対にしないでください。
ジェームズ

1
@ジェームズそれは興味深い議論でした。:) chat.stackexchange.com/rooms/2175実装が、コンポーネントが他のコンポーネントに興味があることについてあまりにも多く知っているというのが私の最大の不満です。今後も議論を続けたいと思います。
ポールマンタ

14

あなたはすでに答えを受け入れましたが、CBSでの私の刺し傷です。ジェネリックComponentクラスにはいくつかの制限があることがわかったので、GDC 2009でRadical Entertainmentが説明したデザインを使用AttributesしましたBehaviors。(「ゲームオブジェクトコンポーネントアーキテクチャの理論と実践」、Marcin Chady)

2ページのドキュメントで設計上の決定事項を説明します。ここにすべてを貼り付けるには長すぎるため、リンクを投稿します。現在は、ロジックコンポーネント(レンダリングコンポーネントと物理コンポーネントも対象外)のみを対象としていますが、私がやろうとしたことの概要がわかるはずです。

►http ://www.pdf-archive.com/2012/01/08/entity-component-system/preview/page/1

ドキュメントからの抜粋は次のとおりです。

要するに属性と動作

Attributes1つのカテゴリのデータを管理し、それらが持つロジックのスコープは制限されます。たとえばHealth、現在の値が最大値を超えないようにし、現在の値が特定のクリティカルレベルを下回ったときに他のコンポーネントに通知することもできますが、それ以上の複雑なロジックは含まれません。またはにAttributes依存していません。AttributesBehaviors

Behaviorsエンティティがゲームイベントにどのように反応するかを制御し、決定を下し、Attributes必要に応じての値を変更します。Behaviorsはの一部に依存していますが、Attributes相互に直接やり取りすることはできません。Attributes’相手による値の変更方法Behaviorsと送信されたイベントにのみ反応します。


編集:そして、コンポーネントが相互に通信する方法を示す関係図を次に示します。

属性と動作間のコミュニケーション図

実装の詳細:エンティティレベルEventManagerは、使用される場合にのみ作成されます。Entityクラスはただのポインタを格納しEventManagerた場合にのみ、いくつかのコンポーネントの要求に初期化されています。


編集:別の質問で、私はこれに同様の答えをしました。あなたは、おそらく、のためにここにシステムのより良い説明をそれを見つけることができます。
/gamedev//a/23759/6188


2

本当に必要なプロパティとそれらが必要な場所に依存します。使用するメモリ量と使用する処理能力/タイプ。私は次のことを見て、しようとしました:

  • 複数のコンポーネントで使用されているが、1つだけ変更されたプロパティは、そのコンポーネントに保存されます。シェイプは、AIシステム、物理システム、レンダリングシステムがベースシェイプにアクセスする必要があるゲームの良い例です。それは重いプロパティであり、可能な場合は1か所にのみ残る必要があります。
  • 位置などのプロパティを複製する必要がある場合があります。たとえば、複数のシステムを並行して実行する場合、システム間でのピークを回避し、位置を同期します(マスターコンポーネントからコピーするか、必要に応じてデルタまたは衝突パスを使用して同期します)。
  • コントロールまたはAIの「意図」に由来するプロパティは、外部から見えなくても他のシステムに適用できるため、専用システムに保存できます。
  • 単純なプロパティは複雑になる可能性があります。多くのデータを共有する必要がある場合、位置に専用システムが必要になることがあります(位置、方向、フレームデルタ、現在の合計デルタ移動、現在のフレームと前のフレームの差分移動、回転...)。その場合、システムにアクセスして専用コンポーネントから最新のデータにアクセスする必要があり、アキュムレーター(デルタ)を介して変更する必要がある場合があります。
  • プロパティが生の配列(double *)に保存される場合があり、コンポーネントには、異なるプロパティを保持する配列へのポインタのみが含まれます。最も明白な例は、大規模な並列計算(CUDA、OpenCL)が必要な場合です。そのため、ポインタを適切に管理する1つのシステムを用意することは、ほんの一握りの場合があります。

これらの原則には制限があります。もちろん、ジオメトリをレンダラーにプッシュする必要がありますが、おそらくそこから取得したくないでしょう。マスタージオメトリは、変形が発生した場合に物理エンジンに保存され、レンダラーと同期します(オブジェクトの距離に応じて随時)。だからとにかくそれを複製します。

完璧なシステムはありません。また、よりシンプルなシステムでより良いゲームもあれば、システム間でより複雑な同期が必要なゲームもあります。

最初に、コンポーネントからすべてのプロパティに簡単な方法でアクセスできることを確認して、システムの微調整を開始したら、プロパティを透過的に保存する方法を変更できるようにします。

一部のプロパティをコピーするのに恥はありません。いくつかのコンポーネントがローカルコピーを保持する必要がある場合、「外部」値にアクセスするよりもコピーして同期する方が効率的な場合があります。

また、同期はすべてのフレームで発生する必要はありません。一部のコンポーネントは、他のコンポーネントよりも低い頻度で同期できます。多くの場合、レンダリングコンポーネントは良い例です。プレーヤーと対話しないものは、遠くにあるものと同じように、あまり頻繁に同期できません。カメラフィールドの外側と外側のフレームは、より少ない頻度で同期できます。


サイズコンポーネントに関しては、おそらく位置コンポーネント内にバンドルできます。

  • サイズを持つすべてのエンティティに物理コンポーネント、たとえば領域があるわけではないので、物理コンポーネントを物理コンポーネントにバンドルすることは最適ではありません。
  • サイズはおそらく位置なしでは重要ではありません
  • 位置を持つすべてのオブジェクトにはおそらくサイズがあります(スクリプト、物理学、AI、レンダリングなどに使用できます)。
  • サイズはおそらくサイクルごとに更新されるわけではありませんが、位置は更新される可能性があります。

サイズの代わりにサイズ修飾子を使用することもできます。

すべてのプロパティを汎用プロパティストレージシステムに格納することについて...正しい方向に進んでいるかどうかはわかりません...ゲームの中心となるプロパティに焦点を当て、できるだけ多くの関連プロパティをバンドルするコンポーネントを作成します。これらのプロパティへのアクセスを適切に抽象化する限り(たとえば、プロパティを必要とするコンポーネントのゲッターを介して)、ロジックをあまり壊さずに、後で移動、コピー、および同期できる必要があります。


2
ところで、私の現在の担当者は11月9日から666なので、私を+1または-1しています...気味が悪いです。
コヨーテ

1

コンポーネントをエンティティに任意に追加できる場合、特定のコンポーネントがエンティティに存在するかどうかを照会し、その参照を取得する方法が必要です。したがって、目的のオブジェクトが見つかるまでObjectComponentから派生したオブジェクトのリストを反復処理し、それを返すことができます。ただし、正しい型のオブジェクトを返します。

C ++またはC#では、これは通常、のようなエンティティにテンプレートメソッドがあることを意味しますT GetComponent<T>()。そして、その参照を取得すると、それが保持するメンバーデータを正確に把握できるため、直接アクセスするだけです。

LuaやPythonのようなものでは、必ずしもそのオブジェクトの明示的な型を持つ必要はなく、おそらくどちらも気にしません。ただし、ここでも、メンバー変数にアクセスして、存在しないものにアクセスしようとすると発生する例外を処理できます。

オブジェクトプロパティのクエリは、静的に型付けされた言語のコンパイル時または動的に型付けされた言語の実行時に、言語が実行できる作業を明示的に複製するように聞こえます。


エンティティから厳密に型指定されたコンポーネントを取得することを理解しています(ジェネリックなどを使用)、私のプロパティはそれらのプロパティがどこに行くべきか、特にプロパティが複数のコンポーネントによって使用され、単一のコンポーネントがそれを所有しているとは言えません 質問に関する3番目と4番目のコメントを参照してください。
ジョージダケット

適合する場合は既存のものを選択し、適合しない場合はプロパティを新しいコンポーネントに組み込みます。たとえば、Unityには位置だけである「変換」コンポーネントがあり、オブジェクトの位置を変更する必要がある場合は、そのコンポーネントを介してそれを行います。
キロタン

1

「それから、私の問題は共通のプロパティをどこに保存するかだと思います。たとえば、RenderingComponentとPhysicsComponentによって使用される位置としてのオブジェクト。プロパティを配置する場所の決定を過剰に考えていますか?どちらかで、他のクエリが必要なプロパティを持つコンポーネントのオブジェクトを持っていますか?」

問題は、RenderingComponent 位置を使用することですが、PhysicsComponent それを提供します。使用するプロバイダーを各ユーザーコンポーネントに伝える方法が必要です。理想的には不可知論的な方法で、そうでなければ依存関係があります。

「...私の質問は、これらのプロパティがどこに行くべきか、特にプロパティが複数のコンポーネントによって使用され、単一のコンポーネントがそれを所有しているとは言えない場合に関するものです。質問に関する私の3番目と4番目のコメントを参照してください」

共通のルールはありません。特定のプロパティに依存します(上記を参照)。

いがコンポーネントベースのアーキテクチャでゲームを作成し、それをリファクタリングします。


私は何を理解しているとは思わない PhysicsComponentすべきを。私はそれを物理環境内のオブジェクトのシミュレーションを管理するものと考えていますが、これは私をこの混乱に導きます:レンダリングする必要があるすべてのものをシミュレーションする必要はないので、位置を含むため追加するPhysicsComponentときに追加するのは間違っているようですRenderingComponentそのRenderingComponent用途。私は、相互接続されたコンポーネントのウェブで終わる自分自身を簡単に見ることができました。つまり、各エンティティにすべて/ほとんどを追加する必要があります。
ジョージダケット

私は実際に似たような状況にありました:)。PhysicsBodyComponentとSimpleSpatialComponentがあります。どちらも位置、角度、サイズを提供します。しかし、最初のものは物理シミュレーションに参加し、関連する追加のプロパティがあり、2番目のものはその空間データを保持しています。独自の物理エンジンがある場合は、後者から前者を継承することもできます。
デン

「相互接続されたコンポーネントのウェブで終わる自分自身を簡単に見ることができました。つまり、各エンティティにすべて/ほとんどを追加する必要があります。」これは、実際のゲームプロトタイプがないためです。ここでは、いくつかの基本的なコンポーネントについて説明しています。どこでも使用されるのも不思議ではありません。
デン

1

あなたの腸のは、持っていることを知らせるThingPropertyThingComponentThingManagerすべてのためにThing、成分a少しやり過ぎの種類を。私はそれが正しいと思います。

ただし、どのシステムがそれらを使用しているのか、どのエンティティに属しているのかなど、関連するコンポーネントを追跡する方法が必要です。

TransformPropertyかなり一般的なものになるでしょう。しかし、レンダリングシステムを担当しているのは誰ですか?物理システム?サウンドシステム?Transformコンポーネントがそれ自体を更新する必要があるのはなぜですか?

解決策は、ゲッター、セッター、およびイニシャライザー以外のプロパティからコードを削除することです。コンポーネントは、レンダリング、AI、サウンドの再生、動きなどのさまざまなタスクを実行するためにゲーム内のシステムによって使用されるデータです。

Artemisについて読む:http : //piemaster.net/2011/07/entity-component-artemis/

そのコードを見ると、依存関係をのリストとして宣言するシステムに基づいていることがわかりますComponentTypes。各Systemクラスを記述し、constructor / initメソッドで、システムが依存する型を宣言します。

レベルまたはその他のセットアップ中に、エンティティを作成し、それらにコンポーネントを追加します。その後、そのエンティティにArtemisに報告するように指示すると、Artemisはそのエンティティの構成に基づいて、どのシステムがそのエンティティについて知ることに関心を持つかを判断します。

次に、ループの更新フェーズで、Systemsに更新するエンティティのリストがあります。今あなたが狂ったシステムを考案することができるので、外のビルドエンティティをコンポーネントの粒度を持つことができModelComponentTransformComponentFliesLikeSupermanComponent、とSocketInfoComponent、とmakeに似た奇妙な何か空飛ぶ円盤マルチプレイヤーゲームに接続しているクライアント間のハエそれを行います。さて、そうではないかもしれませんが、アイデアは、物事を切り離して柔軟に保つことです。

Artemisは完全ではなく、サイトの例は少し基本的ですが、コードとデータの分離は強力です。あなたがそれを正しく行うなら、それはあなたのキャッシュにも良いです。アルテミスはおそらくその前線でそれをしませんが、そこから学ぶのは良いことです。

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