「ゲームオブジェクト」-およびコンポーネントベースのデザイン


25

私は過去3〜4年間、趣味のプロジェクトに取り組んでいます。シンプルな2Dおよび3Dゲーム。しかし最近、より大きなプロジェクトを始めました。ここ数か月間、私はすべてのゲームオブジェクトのベースとなるゲームオブジェクトクラスを設計しようとしていました。何度も試行錯誤を繰り返した後、グーグルに目を向けると、すぐにいくつかのGDC PDFおよびPowerPointが示されました。そして今、私はコンポーネントベースのゲームオブジェクトを把握しようとしています。

エンジンはゲームオブジェクトを作成し、その後、健康、物理学、ネットワークなどの処理を行うさまざまなコンポーネントをアタッチすることを理解しています。しかし、私が理解していないのは、コンポーネントXがYがオブジェクトの状態を変更したかどうかを知る方法です。健全性はHealthComponentによって制御されているため、PhysicsComponentはプレーヤーが生きているかどうかをどのように知るのでしょうか。そして、HealthComponentはどのようにして「プレイヤーが死んだアニメーション」を再生しますか?

私はそれが次のようなものであると感じていました(HealthComponent内):

if(Health < 0) {
   AnimationComponent.PlayAnimation("played-died-animation")
}

しかし、もう一度、HealthComponentは、どのように接続されているゲームオブジェクトにAnimationComponentが接続されていることを知るのでしょうか?私がここで見る唯一の解決策は

  1. AnimationComponentがアタッチされているかどうかを確認してください(コンポーネントコード内またはエンジン側)

  2. コンポーネントには他のコンポーネントが必要ですが、それはコンポーネント設計全体と戦うようです。

  3. HealthWithAnimationComponent、HealthNoAnimationComponentなどのように書きます。これもコンポーネント設計のアイデア全体と戦うようです。


1
質問が大好きです。私は同じ数ヶ月前に尋ねるべきでしたが、それに決して近づきませんでした。私が直面した追加の問題は、ゲームオブジェクトに同じコンポーネントの複数のインスタンスがある場合です(たとえば、複数のアニメーション)。答えがそれに触れることができれば素晴らしいと思います。最終的には、Gameオブジェクトのすべてのコンポーネント間で変数を共有して、通知にメッセージを使用することになりました(したがって、変数の値を取得するためにメッセージを送信する必要はありません)。
ADB

1
ゲームの種類によっては、おそらく健康コンポーネントを持ち、アニメーションコンポーネントを持たないゲームオブジェクトはないでしょう。そして、これらのゲームオブジェクトはすべて、おそらくUnitのようなものを表しています。したがって、ヘルスコンポーネントを破棄し、フィールドヘルスを持つUnitComponentを作成し、ユニットに必要なすべてのコンポーネントを把握できます。コンポーネントのこの粒度は、実際には何の役にも立ちません-ドメインごとに1つのコンポーネント(レンダリング、オーディオ、物理、ゲームロジック)を持つ方が現実的です。
喜界丸

回答:


11

すべての例に、ひどい問題があります。正常性コンポーネントは、エンティティの死に応答する必要がある可能性のあるすべてのコンポーネントタイプについて知る必要があります。したがって、どのシナリオも適切ではありません。エンティティにはヘルスコンポーネントがあります。アニメーションコンポーネントがあります。他の人に依存したり、他の人について知りません。彼らはメッセージングシステムを介して通信します。

正常性コンポーネントは、エンティティが「死亡」したことを検出すると、「死亡」メッセージを送信します。適切なアニメーションを再生してこのメ​​ッセージに応答するのは、アニメーションコンポーネントの責任です。

正常性コンポーネントは、メッセージをアニメーションコンポーネントに直接送信しません。たぶんそれはそのエンティティのすべてのコンポーネント、おそらくシステム全体にブロードキャストします。おそらく、アニメーションコンポーネントは、「私が死んだ」メッセージに関心があることをメッセージングシステムに知らせる必要があります。メッセージングシステムを実装するには多くの方法があります。どのように実装しても、重要なのは、ヘルスコンポーネントとアニメーションコンポーネントが他のコンポーネントが存在するかどうかを知る必要がないことです。新しいコンポーネントを追加しても、適切なメッセージを送信するために既存のコンポーネントを変更する必要はありません。


オーケー、これは理にかなっています。しかし、「死んだ」、「ポータルが壊れている」などの「状態」を宣言するのは誰ですか。コンポーネントまたはエンジンですか。ヘルスコンポーネントが接続されることのない状態に「死んだ」状態を追加することは、私にとってはちょっと無駄に思えます。私はただ飛び込み、いくつかのコードをテストし、何が機能するのかを見始めると思います。
hayer

マイケルとパトリックヒューズは上記の正しい答えを持っています。コンポーネントは単なるデータです。エンティティが死んでメッセージを送信したことを検出するのは、実際にはヘルスコンポーネントではなく、ゲーム固有のロジックのより高いレベルの部分です。それを抽象化する方法はあなた次第です。実際の死の状態をどこにでも保存する必要はありません。ヘルスが0未満の場合、オブジェクトは死んでおり、ヘルスコンポーネントは「動作なし」を壊すことなくデータチェックロジックのそのビットをカプセル化できます。コンポーネントの状態を動作として変更するもののみを考慮する場合の制限。
ブレッキ

好奇心が強い、MovementComponentをどのように処理しますか?入力を検出したら、PositionComponentの速度を上げる必要があります。メッセージはどのように見えますか?
ヒント48 14

8

方法アルテミスは、問題を解決には、コンポーネント内の処理をしないことです。コンポーネントには、必要なデータのみが含まれます。システムは複数のコンポーネントタイプを読み取り、必要な処理を実行します。

したがって、あなたの場合、HealthComponent(およびその他)を読み取り、キューを適切なアニメーションで再生するRenderSystemがあります。この方法でデータを関数から分離すると、依存関係を適切に管理しやすくなります。


これは最終的に問題を処理する良い方法になります。コンポーネントはプロパティを表し、システムは異なるプロパティを結び付け、それらを使用して作業します。それは)=離れて、伝統的なOOPの考え方から、巨大なシフトだと一部の人々の頭の傷を作る
パトリック・ヒューズ

Okey、今私は本当に迷っています。「対照的に、ESでは、それぞれがエンティティによって表される戦場に100ユニットがある場合、ユニットで呼び出すことができる各メソッドのコピーはありません。エンティティにはメソッドが含まれず、コンポーネントにもメソッドが含まれません。代わりに、アスペクトごとに外部システムがあり、その外部システムには、これと互換性があるとマークするコンポーネントを所有するエンティティで呼び出すことができるすべてのメソッドが含まれますシステム。" さて、GunComponentのデータはどこに保存されますか?ラウンドなど。すべてのエンティティが同じコンポーネントを共有する場合。
hayer

1
私が理解している限り、すべてのエンティティが同じコンポーネントを共有していないため、各エンティティにN個のコンポーネントインスタンスをアタッチできます。次に、システムはゲームにクエリを実行して、関心のあるコンポーネントインスタンスが関連付けられているすべてのエンティティのリストを
ジェイクウッズ

これにより、問題が回避されます。システムはどのコンポーネントを使用するかをどのように知るのですか?システムには他のシステムも必要になる場合があります(たとえば、StateMachineシステムはアニメーションを呼び出す必要がある場合があります)。ただし、WHOがデータを所有しているという問題は解決します。実際、より単純な実装は、ゲームオブジェクトに辞書を持ち、各システムがそこに変数を作成することです。
ADB

それは問題を移動させますが、より住みやすい場所に移動します。システムには、関連するコンポーネントが配線されています。システムは、コンポーネントを介して相互に通信できます(StateMachineは、Animationが何をすべきかを知るために読み込むコンポーネント値を設定できます(または、イベントを発生させることができます)。コンポーネントは、関連するプロパティが一緒にグループ化され、それらが静的に確認することができるということであるあなたが別で、「ダメージ」を使用して一つの場所に「Dammage」を追加、それを取得しようとしたん奇妙なエラーがあるため。。
マイケル・

6

あなたのコードでは、オブジェクトが状態を変更したかどうかを知る方法(私はそれらを使用し、おそらく他のいくつかの方法が存在する)があります:

  1. メッセージを送る。
  2. コンポーネントからデータを直接読み取ります。

1)AnimationComponentがアタッチされているかどうかを確認します(コンポーネントコード内またはエンジン側)

これには、1。GameObjectのHasComponent関数、または2.コンポーネントをアタッチするときに、何らかの構築関数の依存関係を確認できます。

2)コンポーネントには他のコンポーネントが必要ですが、それはコンポーネント設計全体と戦うようです。

私が読んだいくつかの記事では、Idealシステムコンポーネントは相互に依存していませんが、実際にはそうではありません。

3)HealthWithAnimationComponent、HealthNoAnimationComponentなどのように記述します。これもコンポーネント設計のアイデア全体と戦うようです。

そのようなコンポーネントを記述するのは悪い考えです。私のアプリでは、最も独立したHealthコンポーネントを作成しました。現在、特定のイベント(「ヒット」、「修復」など)についてサブスクライバーに通知するObserverパターンについて考えています。そのため、AnimationComponentはアニメーションをいつ再生するかを自ら決定する必要があります。

しかし、CBESに関する記事を読んだときは感銘を受けたので、CBESを使用してその新しい可能性を発見したとき、私は今とても幸せです。


1
さて、google.no / @スライド16
hayer

@ bobenko、CBESに関する記事へのリンクをお願いします。私も非常に興味深いです;)
エドワード83年

1
そして、lambdor.net / ?p = 171 @ bottom、私の質問のこの種の要約相対的な複雑な非基本コンポーネントの観点から異なる機能をどのように定義できますか?最も基本的なコンポーネントは何ですか?基本コンポーネントは純粋な関数とどのように異なりますか?既存のコンポーネントは、新しいコンポーネントからの新しいメッセージと自動的に通信できますか?コンポーネントが知らないメッセージを無視する意味は何ですか?結局のところ、入力プロセス出力モデルはどうなりましたか?
-hayer

1
ここにCBESの良い答えがありますstackoverflow.com/a/3495647/903195私が調査した記事のほとんどはこの答えからです。cowboyprogramming.com/2007/01/05/evolve-your-heirachyから始めてインスピレーションを得た後、In Gems 5(覚えているように)に例のある良い記事がありました。
イェブン

しかし、機能的反応型プログラミングの別の概念についてはどうでしょうか。私にとってこの質問はまだ開かれていますが、あなたにとっては研究のための良い方向です。
イェブン

3

マイケル、パトリックヒューズ、ブレッキのようなものです。問題を単純に回避することを回避する解決策は、そもそも問題を引き起こすイデオロギーを放棄することです。

OODが少なく、関数型プログラミングに似ています。コンポーネントベースの設計を試し始めたとき、私はこの問題を将来見つけました。私はもう少しグーグルで検索しましたが、「Functive Reactive Programming」が解決策であることがわかりました。

現在、私のコンポーネントは、現在の状態を記述する変数とフィールドのコレクションにすぎません。次に、それらに関連するすべてのコンポーネントを更新する「システム」クラスがたくさんあります。リアクティブ部分は、システムを適切に定義された順序で実行することにより実現されます。これにより、システムが処理および更新を行うために次に並んでいるもの、および読み取りおよび更新しようとしているコンポーネントおよびエンティティーが常に最新のデータで動作することが保証されます。

ただし、ある意味では、問題が再び動いたと主張することもできます。システムを実行する必要がある順序が単純でない場合はどうでしょうか?循環的な関係があり、if-elseとswitchステートメントの混乱をじっと見つめる前の時間の問題がある場合はどうでしょうか。暗黙のメッセージング形式ですよね?一見、小さなリスクだと思います。通常、物事は順番に処理されます。プレイヤー入力->エンティティ位置->衝突検出->ゲームロジック->レンダリング->最初からやり直してください。その場合、それぞれに1つのシステムがあり、各システムにupdate()メソッドを提供してから、それらをゲームループで順番に実行します。

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