作成したすべてのゲームについて、すべてのゲームオブジェクト(行頭文字、車、プレーヤー)を単一の配列リストに配置し、ループして描画と更新を行います。各エンティティの更新コードは、そのクラス内に保存されます。
私は疑問に思っていましたが、これは物事を進める正しい方法ですか、それともより速く、より効率的な方法ですか?
作成したすべてのゲームについて、すべてのゲームオブジェクト(行頭文字、車、プレーヤー)を単一の配列リストに配置し、ループして描画と更新を行います。各エンティティの更新コードは、そのクラス内に保存されます。
私は疑問に思っていましたが、これは物事を進める正しい方法ですか、それともより速く、より効率的な方法ですか?
回答:
正しい方法はありません。記述していることは機能しますが、パフォーマンスの問題を引き起こしているのではなく、設計に関心があるので、質問しているように見えるので、おそらく十分に高速です。だから、それは確かに、正しい方法。
たとえば、各オブジェクトタイプの同種リストを維持するか、異種リストのすべてをグループ化して、キャッシュ内のコードの一貫性を改善したり、並列更新を許可したりすることで、確実に異なる方法で行うことができます。これを行うことでかなりのパフォーマンスの改善が見られるかどうかは、ゲームの範囲と規模を知らずに言うのは困難です。
また、エンティティの責任をさらに除外することもできます。つまり、エンティティは現在、更新とレンダリングの両方を行っているように思われます。これにより、個々のインターフェイスを相互に分離することで、個々のインターフェイスの保守性と柔軟性を高めることができます。繰り返しになりますが、必要な追加作業の恩恵を受けるかどうかは、あなたのゲームについて私が知っていることを知るのは難しいです(基本的には何もありません)。
パフォーマンスや保守性の問題に気付いた場合、改善の余地があるかもしれないということについて、あまり強調しすぎないことをお勧めします。
他の人が言ったように、それが十分に速いなら、それは十分に速いです。より良い方法があるかどうか疑問に思っているなら、自問する質問は
すべてのオブジェクトを繰り返し処理しますが、それらのサブセットのみを操作しますか?
もしそうなら、それはあなたが関連する参照だけを保持している複数のリストを持ちたいかもしれないことを示しています。たとえば、すべてのゲームオブジェクトをループしてレンダリングするが、オブジェクトのサブセットのみをレンダリングする必要がある場合、レンダリングする必要のあるオブジェクトのみに個別のレンダリング可能リストを保持する価値があります。
これにより、ループするオブジェクトの数が削減され、リスト内のすべてのオブジェクトが処理されることが既にわかっているため、不要なチェックがスキップされます。
パフォーマンスを大幅に向上できるかどうかを確認するには、プロファイルを作成する必要があります。
特定のゲームのニーズにほぼ完全に依存します。単純なゲームでは完全に機能しますが、より複雑なシステムでは機能しません。ゲームで機能している場合は、心配する必要はありません。必要になるまで、過剰なエンジニアリングを試みないでください。
単純なアプローチが状況によって失敗する理由については、可能性は無限であり、ゲーム自体に完全に依存しているため、1つの投稿にまとめるのは困難です。他の回答では、たとえば、責任を共有するオブジェクトを別のリストにグループ化することができます。
これは、ゲームデザインに応じて行う最も一般的な変更の1つです。他のいくつかの(より複雑な)例について説明する機会がありますが、他にも多くの考えられる理由と解決策があることを忘れないでください。
まず、ゲームオブジェクトの更新とレンダリングは、ゲームによっては要件が異なる場合があるため、個別に処理する必要があることを指摘します。ゲームオブジェクトの更新とレンダリングで発生する可能性のある問題:
ゲームオブジェクトの更新
これは、私が読むことをお勧めするGame Engine Architectureからの抜粋です。
オブジェクト間の依存関係が存在する場合、上記の段階的な更新手法を少し調整する必要があります。これは、オブジェクト間の依存関係により、更新の順序を管理する規則が競合する可能性があるためです。
つまり、一部のゲームでは、オブジェクトが互いに依存し、特定の更新順序を必要とする可能性があります。このシナリオでは、オブジェクトとそれらの相互依存関係を保存するために、リストよりも複雑な構造を考案する必要があるかもしれません。
ゲームオブジェクトのレンダリング
一部のゲームでは、シーンとオブジェクトが親子ノードの階層を作成し、正しい順序でレンダリングされ、親を基準にした変換が必要です。そのような場合、単一のリストではなく、シーングラフ(ツリー構造)などのより複雑な構造が必要になる場合があります。
他の理由としては、たとえば、空間分割データ構造を使用して、視錐台カリングの実行効率を向上させる方法でオブジェクトを整理することがあります。
答えは「はい、これで構いません」です。パフォーマンスが交渉不可能なビッグアイアンシミュレーションに取り組んだとき、すべてが1つの大きなファット(BIGおよびFAT)グローバルアレイに含まれていることに驚きました。その後、OOシステムに取り組み、2つを比較しました。非常にいものでしたが、デバッグでコンパイルされたシングルスレッドプレーンオールドCの「すべてを支配する1つの配列」バージョンは、C ++で-O2でコンパイルされた「きれいな」マルチスレッドOOシステムよりも数倍高速でした。その大部分は、大脂肪配列バージョンの優れたキャッシュローカリティ(スペースと時間の両方)に関係していると思います。
パフォーマンスが必要な場合、1つの大きなグローバルC配列が(経験から)上限になります。
基本的にあなたがしたいことは、必要に応じてリストを作成することです。一度にすべての地形オブジェクトを再描画する場合、それらのリストだけが便利です。しかし、これには価値がありますが、数万個、および地形ではないものがたくさんあります。それ以外の場合は、すべてのリストを熟読し、「if」を使用して地形オブジェクトを引き出すだけで十分に高速です。
他のリストの数に関係なく、常に1つのマスターリストが必要でした。通常、私はそれをある種のマップ、またはキー付きテーブル、またはディクショナリにし、個々のアイテムにランダムにアクセスできるようにします。ただし、単純なリストの方がはるかに簡単です。ゲームオブジェクトにランダムにアクセスしない場合、マップは必要ありません。また、適切なエントリを見つけるために数千のエントリを時折順次検索するのに時間がかかりません。ですから、他に何をするにしても、マスターリストに固執することを計画していると思います。大きくなったマップの使用を検討してください。(キーの代わりにインデックスを使用できる場合、配列に固執し、Mapよりもはるかに優れたものを持つことができます。私は常にインデックス番号の間に大きなギャップができたので、それをあきらめなければなりませんでした。)
配列について一言:信じられないほど高速です。しかし、約100,000の要素を取得すると、ガベージコレクターに適切なものを提供し始めます。スペースを一度割り当てることができ、後でそれを変更できない場合は、問題ありません。ただし、アレイを継続的に拡張している場合、メモリの大きなチャンクを継続的に割り当てて解放しているため、一度に数秒間ゲームがハングし、メモリエラーで失敗することさえあります。
速くて効率的ですか?確かに。ランダムアクセス用のマップ。本当に大きなリストの場合、ランダムアクセスが必要ない場合は、リンクリストが配列に勝ります。必要に応じて、さまざまな方法でオブジェクトを整理するための複数のマップ/リスト。更新をマルチスレッド化できます。
しかし、それはすべて多くの作業です。その単一の配列は高速でシンプルです。必要な場合にのみ他のリストや物を追加できますが、おそらくそれらは必要ないでしょう。ゲームが大きくなった場合、何がうまくいかないかだけに注意してください。実際のバージョンの10倍の数のオブジェクトを含むテストバージョンを用意して、いつトラブルに向かうのかを考えてください。(ゲームが大きくなっている場合にのみこれを行ってください。)(そして、マルチスレッドを試してみてください。多くのことを考えずに。他のすべては簡単に始められます。マルチスレッドを使用すると、入会するのに大きな代価を払うことになり、入会するための会費が高くなり、退出するのが困難になります。)
つまり、あなたがやっていることをやり続けるだけです。あなたの最大の制限はおそらくあなた自身の時間であり、あなたはそれを最大限に活用しています。
私は、単純なゲームにやや似た方法を使用しました。次の条件に該当する場合、すべてを1つの配列に保存すると非常に便利です。
いずれにせよ、このソリューションは、gameObject_ptr
構造体を含む固定サイズの配列として実装しました。この構造体には、ゲームオブジェクト自体へのポインタと、オブジェクトが「生きている」かどうかを表すブール値が含まれていました。
ブール値の目的は、作成および削除操作を高速化することです。シミュレーションから削除されたゲームオブジェクトを破棄する代わりに、単に "alive"フラグをに設定しfalse
ます。
オブジェクトで計算を実行すると、コードは配列をループし、「生存」とマークされたオブジェクトで計算を実行し、「生存」とマークされたオブジェクトのみがレンダリングされます。
もう1つの簡単な最適化は、オブジェクトタイプごとに配列を整理することです。たとえば、すべての箇条書きは、配列の最後のn個のセルに存在できます。次に、インデックスの値または配列要素へのポインタでintを保存することにより、特定の型を低コストで参照できます。
言うまでもなく、このソリューションは配列が許容できないほど大きくなるため、大量のデータを含むゲームには適していません。また、未使用のオブジェクトがメモリを占有することを禁止するメモリ制約があるゲームには適していません。要するに、ある時点で持つことができるゲームオブジェクトの数に既知の上限がある場合、それらを静的配列に格納しても何も問題はありません。
これが私の最初の投稿です!それがあなたの質問に答えることを願っています!
異常ではありますが、許容範囲内です。「より高速」および「より効率的」などの用語は相対的です。通常、ゲームオブジェクトを個別のカテゴリ(箇条書き用の1つのリスト、敵用の1つのリストなど)に整理して、プログラミング作業をより整理します(したがって、より効率的になります)が、コンピューターがすべてのオブジェクトをより速くループするわけではありません。
単一の配列とオブジェクトを言います。
だから(見るコードなしで)それらはすべて 'uberGameObject'に関連していると思います。
したがって、2つの問題があります。
1)あなたは「神」オブジェクトを持っているかもしれません-それは実際にそれが何であるかによってセグメント化されていない、たくさんのものをします。
2)このリストを繰り返し処理して、入力しますか?またはそれぞれを開梱します。これには大きなパフォーマンスの低下があります。
さまざまなタイプ(箇条書き、悪者など)を独自の厳密に型指定されたリストに分割することを検討してください。
List<BadGuyObj> BadGuys = new List<BadGuyObj>();
List<BulletsObj> Bullets = new List<BulletObj>();
//Game
OnUpdate(gameticks gt)
{ foreach (BulletObj thisbullet in Bullets) {thisbullet.Update();} // much quicker.
update()
。
一般的な単一のリストと各オブジェクトタイプの個別のリストとの間で妥協案を提案できます。
複数のリスト内の1つのオブジェクトへの参照を保持します。これらのリストは、基本動作によって定義されます。例えば、MarineSoldier
オブジェクトは、で参照されPhysicalObjList
、GraphicalObjList
、UserControlObjList
、HumanObjList
。そのため、物理学を処理するPhysicalObjList
とき、毒でダメージを与えるプロセスを繰り返します- HumanObjList
兵士が死んだ場合-からそれを削除しますが、HumanObjList
そのままにしPhysicalObjList
ます。このメカニズムを機能させるには、オブジェクトの作成/削除時に自動挿入/削除オブジェクトを整理する必要があります。
アプローチの利点:
AllObjectsList
更新時のキャストなし)MegaAirHumanUnderwaterObject
そのタイプの新しいリストを作成する代わりに対応するリストに挿入されます)PS:自己更新に関しては、複数のオブジェクトが相互作用し、それぞれが他のオブジェクトに依存している場合に問題が発生します。これを解決するには、自己更新ではなく、オブジェクトに外部的に影響を与える必要があります。