構成が重いOOP対純粋なエンティティコンポーネントシステム?[閉まっている]


13

私は認めます、私は相続財産を濫用し、さらには虐待の罪を犯しました。OOPコースを受講するときに作成した最初の(テキスト)ゲームプロジェクトは、「ドア」と「1つのドアがある部屋」、「2つのドアがある部屋」から「ロックされたドア」と「ロック解除されたドア」 「部屋」から。

私が最近取り組んだ(グラフィカルな)ゲームでは、自分のレッスンを学び、継承の使用に制限をかけたと思いました。しかし、問題がすぐに現れ始めたことに気付きました。私のルートクラスはどんどん肥大化し始め、リーフクラスは重複したコードでいっぱいになりました。

私はまだ間違ったことをしていると思っていたので、オンラインで調べたところ、この問題を抱えているのは自分だけではないことがわかりました。いくつかの徹底的な調査の後、エンティティシステムを発見することになりました(読む:googlefu)

読み始めたとき、コンポーネントを使用した従来のOOP階層で発生していた問題をどれだけ明確に解決できるかを知ることができました。ただし、これらは最初の測定値でした。もっとつまずいたとき… T-machineのような「ラジカルな」ESアプローチ。

私は彼らが使っていた方法に反対し始めました。純粋なコンポーネントシステムは過剰であるか、直感的ではなく、おそらくOOPの強さのように見えました。著者は、ESシステムはOOPの反対であると言っており、OOPに沿って使用できるかもしれませんが、実際には使用すべきではありません。私はそれが間違っていると言っているわけではありませんが、実装したい解決策のような気がしませんでした。

ですから、私にとって、そして私の直感に反することなく、投稿の冒頭で抱えていた問題を解決するには、まだ階層を使用することですが、以前に使用したようなモノリシックな階層ではなく、ポリリシックなもの(モノリシックの反対語が見つかりませんでした)は、いくつかの小さな木で構成されています。

次の例は、私が何を意味するかを示しています(これは、Game Engine Architectureの第14章で見つけた例から着想を得ています)。

車両用の小さなツリーがあります。ルート車両クラスには、レンダリングコンポーネント、衝突コンポーネント、位置コンポーネントなどが含まれます。

そして、車両のサブクラスであるタンクは、そこからこれらのコンポーネントを継承し、独自の「キャノン」コンポーネントが与えられます。

キャラクターについても同じことが言えます。キャラクターは独自のコンポーネントを持ち、プレイヤークラスはそれを継承し、入力コントローラーを与えられますが、他の敵クラスはキャラクタークラスを継承し、AIコントローラーを与えられます。

この設計には問題はありません。純粋なEntity Controller Systemを使用していないにもかかわらず、バブリングアップ効果と大きなルートクラスの問題はマルチツリー階層を使用することで解決され、葉が重複しないため、葉を複製する重いコードの問題はなくなりました開始するコードは、コンポーネントだけです。リーフレベルに変更を加える必要がある場合、コードをどこにでもコピーペーストするのではなく、単一のコンポーネントを変更するのと同じくらい簡単です。

もちろん、私と同じように経験が浅いので、最初に単一階層の継承の重いモデルを使用し始めたときに問題は見られなかったため、現在実装を考えているモデルに問題がある場合は、それを見ることができます。

あなたの意見は?

PS:私はJavaを使用しているため、通常のコンポーネントを使用する代わりに、多重継承を使用してこれを実装することはできません。

PPS:コンポーネント間の通信は、依存コンポーネントを相互にリンクすることにより行われます。これは結合につながりますが、私はそれが良いトレードオフだと思います。


2
これはすべて私には問題ないようです。階層またはエンティティシステムに、「匂いがする」特定の例はありますか?最終的には、純粋さの感覚以上にゲームを完成させることがすべてです。
drhayes

私はそうはしませんが、私が言ったように、私はほとんど経験がなく、このための最高の裁判官からはほど遠いです。
緑流

3
この質問は主観的であり、幅広い議論を受け入れられるため、この質問を終了することに投票しました。誠実な立場から尋ねられたことに感謝します-しかし、ここで進む方法を選択することは完全に個人的な意見の問題です。コンポーネントをサブクラスに修正することの唯一の本当の欠点は、実行時にコンポーネントの配置が変更できないことです。
キロタン

4
私も閉会に投票しました。これはよく書かれた適切な質問ですが、GDSEには適していません。GameDev.netで再投稿してみてください。
ショーンミドルディッチ

書かれているように、「あなたの意見は?」という質問で、それは確かに無制限で答えられません。しかし、あるあなたが質問あるかのようにそれに答える場合は、「任意のぽっかりトラップは、私が気付かずに陥る可能性があることがありますか?」でした釈明 (少なくとも、実際にはさまざまなアーキテクチャ間に経験的な違いがあると思う場合。)
ジョンカルスビーク

回答:


8

この例を考えてみましょう:

RTSを作成しています。完全なロジックに適合するように、ベースクラスを作成し、GameObject次に2つのサブクラスを作成BuildingUnitます。もちろん、これはうまく機能し、次のような結果になります。

  • GameObject
    • ModelComponent
    • CollisionComponent
  • Building
    • ProductionComponent
  • Unit
    • AimingAIComponent
    • WeaponComponent
    • ParticleEmitterComponent
    • AnimationComponent

これでUnit、作成するすべてのサブクラスには、必要なすべてのコンポーネントがすでに含まれています。そしてボーナスとして、あなたはすべてのその面倒なセットアップコード置くための単一の場所持っnewアップだがWeaponComponent、それを接続しているAimingAIComponentParticleEmitterComponent-wonderful!

そしてもちろん、それはまだロジックをコンポーネントに織り込んでいるので、最終的にTowerは建物であるが武器を持つクラスを追加することに決めたときに、それを管理できます。あなたは追加することができAimingAIComponentWeaponComponentParticleEmitterComponentのあなたのサブクラスにBuilding。もちろん、Unitクラスから初期化コードを掘り下げて別の場所に配置する必要がありますが、それは大きな損失ではありません。

そして今、あなたがどれだけ先見を持っていたかに応じて、あなたは微妙なバグに終わるかもしれませんし、そうでないかもしれません。ゲームのどこかに、次のようなコードがいくつかあった可能性があります。

if (myGameObject instanceof Unit)
    doSomething(((Unit)myGameObject).getWeaponComponent());

それはTowerあなたが本当に武器を使って何でも動作することを望んでいたとしても、静かにあなたの建物には動作しません。次のようになります。

WeaponComponent weapon = myGameObject.getComponent(WeaponComponent.class);
if (weapon)
    doSomething(weapon);

ずっといい!これを変更した後、作成したアクセサメソッドのいずれかがこの状況で終わる可能性があります。そのため、最も安全なのはそれらをすべて取り除き、この1つのメソッドを介してすべてのコンポーネントにアクセスgetComponentすることです。このメソッドは任意のサブクラスで機能します。(あるいは、すべての種類をget*Component()スーパークラスに追加GameObjectし、サブクラスでオーバーライドしてコンポーネントを返すことができます。ただし、最初のものを想定します。)

これで、あなたBuildingUnitクラスは、アクセサさえも持たないコンポーネントの単なる袋になります。また、特別なケースの他のオブジェクトとのコードの重複を避けるために、そのほとんどをドラッグアウトする必要があるため、凝った初期化も必要ありません。

これを極端にたどると、常にサブクラスを完全に不活性なコンポーネントのバッグにすることになり、その時点でサブクラスが存在するポイントすらありません。適切なコンポーネントを作成するメソッド階層を備えたクラス階層を持つことで、ほぼすべての利点を得ることができます。代わりに持つのUnitクラスを、あなたは持っているaddUnitComponents作る方法AnimationComponentも呼び出しaddWeaponComponents可能、AimingAIComponentWeaponComponent、とParticleEmitterComponent。その結果、エンティティシステムのすべての利点、階層のすべてのコードの再利用が可能にinstanceofなり、クラスタイプをチェックしたりキャストしたりする誘惑がなくなります。


よく言った。これに追加する理想的な解決策は、各エンティティをスクリプトまたは記述子ファイルで初期化することだと思います。エンティティの初期化にテキストファイルを使用します。それは高速であり、非プログラマーがさまざまなパラメーターをテストできます。
コヨーテ

あなたの投稿を読んでうわー、私に悪夢を与えました!もちろん、良い意味で、あなたがどんな悪夢に直面するのか、目を開いて見ているということです!このような詳細な回答にもっと返信できるといいのですが、本当に追加することはあまりありません!
緑流

より簡単な方法で。あなたは相続人を貧しい人の工場のパターンとして使うことをやめます。
Zardoz89

2

継承より合成がOOPの用語です(少なくとも私が学んだ/教えた方法)。構成と継承を選択するには、「車は車輪の一種です」(継承)と「車には車輪があります」(構成)と言います。一方は他方よりも正確に聞こえるはずです(この場合は組成)。決定できない場合は、他の方法で決定するまでデフォルトで合成します。


1

コンポーネントベースのシステムをゲームオブジェクトに基づいて既存のゲームに統合する場合は、カウボーイプログラミングhttp://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/に記事があります。移行を行う方法。

問題に関しては、モデルはプレイヤーを他の敵とは異なる方法で処理する必要があります。プレイヤーが切断したとき、または特別な場合に他のキャラクターとは異なる扱いをせずに、ステータスを切り替えることはできません(つまり、プレイヤーはAI制御になります)。

異なるエンティティ間でコンポーネントを大量に再利用することとは別に、コンポーネントベースのゲームの考え方は、システム全体の均一性です。各エンティティには、自由にアタッチおよびデタッチできるコンポーネントがあります。同じ種類のコンポーネントはすべて、各種類のインターフェイス(ベースコンポーネント)によって確立された一連の呼び出し(位置、レンダリング、スクリプト...)でまったく同じ方法で処理されます。

ゲームオブジェクトの継承とコンポーネントベースのアプローチを混在させると、コンポーネントベースのアプローチのいくつかの利点と、両方のアプローチの欠点がもたらされます。

それはあなたのために働くことができます。しかし、あるアーキテクチャから別のアーキテクチャへの移行期間以外に両方を混在させることはできませんでした。

PSは電話で書かれているので、外部のリソースにリンクするのに苦労しています。


電話をしていても返事をありがとう!私はあなたの意味を理解しています。移動コンポーネントベースのものは、物事を変更する必要がある柔軟性が高くなります。それが私が先に進み、最小限の継承で作曲アプローチを試みる理由です。(さらに、私が提案したものより。)
みどり竜

@MidoriRyuuは、エンティティオブジェクトのすべての継承を回避します。リフレクション(およびイントロスペクション)が組み込まれた言語を使用できれば幸運です。ファイル(txtまたはXML)を使用して、正しいパラメーターでオブジェクトを初期化できます。あまり手間がかからず、再コンパイルせずにゲームの微調整を強化できます。ただし、少なくともコンポーネント間の通信やその他のユーティリティタスクについては、Entityクラスを保持します。PSはまだ電話中:(
コヨーテ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.