0

私は C++ (多方向 2D スペース シューティング ゲーム) で小さなアーケードのようなゲームを書いており、衝突検出部分を仕上げています。

これが私がそれをどのように整理したかです(私はそれを作ったので、それはくだらないシステムかもしれません):

すべての船は円形のコンポーネントで構成されています。各船のコンポーネントの量は任意です (コンポーネントが多いほど、CPU サイクルが多くなります)。船の作成時に計算する maxComponent 距離があります。これは基本的に、船の中心から最も遠いコンポーネントの端まで描画できる最長の線です。画面上の要素を追跡し、この maxComponentDistance を使用して、衝突するほど接近しているかどうかを確認します。

それらが近接している場合は、異なる船のコンポーネントが交差しているかどうかを確認し始めます。ここで、私の効率性の問題が出てきます。

船の中心に対するコンポーネントの (x,y) 位置がありますが、船が現在どのように回転しているかは考慮されていません。船が移動するたびにコンポーネントを再計算する必要がないため、それらを相対的に保ちます。そのため、回転計算のための小さな式があり、船の中心に対する回転を考慮した位置に対応する 2 次元ベクトルを返します。

衝突検出は GameEngine にあり、2d ベクトルを使用します。私の質問は、戻り値の型についてです。関数が呼び出されるたびに 2d-vector オブジェクトを作成して返す必要がありますか、それとも、そのコンポーネント オブジェクトに追加のプライベート 2d-vector 変数を与え、関数が呼び出されたときにプライベート変数を編集し、そのオブジェクトへのポインターを返す必要がありますか?

メモリ割り当ての効率と、永続的で編集可能なプライベート変数を持つことの効率についてはわかりません。プライベート変数にもメモリを割り当てる必要があることはわかっていますが、衝突をチェックするたびにではなく、新しいコンポーネントが作成されたときだけです。コンポーネントは船が破壊されると削除されるため、私の環境では一定ではありません。

それが私の主なジレンマです。また、実際の衝突検出システムの設計に関する指針もいただければ幸いです。ハックするのは初めてです(少し読んでおくべきだったかもしれません)

前もって感謝します。

4

5 に答える 5

2

ゲッター関数を呼び出すたびにコンポーネント ベクトルのメモリ割り当てを行うことは絶対に避けてください。代わりに、割り当てをできるだけ少なくしてください。たとえば、船のコンポーネントの構成が変更されたとき、またはめったに変更されない場合 (過剰に割り当てられた場合) に行うことができます。

もちろん、メモリプールを調査することもできます。この場合、そのようなコンポーネントを事前に割り当ててプールに入れると、新しいコンポーネントを一定の時間で割り当てることができます。

この種の衝突検出を行うときの一般的なポイント (あまりにも明白な場合は申し訳ありません) として、平方根を計算するのではなく、距離を 2 乗します。:)

于 2008-12-02T07:57:44.067 に答える
1

2D ベクトルが次の場合:

 class Vector2D { double x, y; };

じゃあ絶対返して!例えば:

  Vector2D function( ... );

または参照渡し:

  void function( Vector2D * theReturnedVector2D, ... );

絶対に避けてください:

 vector<double> function(...);

Vector クラス固有の一定のヒープ割り当て/割り当て解除は間違いです!


独自の Vector2D クラスをコピーすることは、計算的に言えば非常に安価です。Vector<> とは異なり、独自の Vector2D クラスには任意のメソッドを組み込むことができます。

過去にこの機能を使用して、distanceToOtherPointSquared()、scanfFromCommandLineArguments()、printfNicelyFormatted()、operator[](int) などのメソッドを組み込みました。


または、そのコンポーネント オブジェクトに追加のプライベート 2d ベクトル変数を与え、関数が呼び出されたときにプライベート変数を編集し、そのオブジェクトへのポインターを返す必要がありますか?

以前のデータを無効にする複数の関数呼び出しに注意してください。それは災害のレシピです !

于 2008-12-03T01:17:54.233 に答える
0
  1. ベクトルを返すだけで開始でき、それをベンチマークできます。誰が知っている、それは十分に速いかもしれません。プロファイラーを使用すると、実行時間がかかる部分を確認することもできます。
  2. メモリ プールを使用してベクトルを再利用し、コピーを減らすことができます
  3. エンジン全体で繰り返される場合は、座標のFlyweight パターンを試して、コピーと割り当てを減らすことができます。
  4. コンポーネントにデータを保持することは、割り当てを減らす良い方法ですが、ベクトルを使用する人は誰でもコンポーネントのライフサイクルに依存するなど、設計に落とし穴が生じます。メモリー・プールの方がおそらく優れています。
于 2008-12-02T08:02:11.390 に答える
0

ヒープとスタックのメモリ割り当てには大きな違いがあります。たとえば、new/delete や malloc/free を使用したヒープへの割り当ては非常に低速です。スタックへの割り当ては非常に高速です。スタックでは、通常、遅い部分はオブジェクトのコピーです。したがって、ベクトルなどには注意してください。ただし、単純な構造を返すことはおそらく問題ありません。

于 2008-12-03T03:20:38.523 に答える
0

2D ベクトルは使用しないでください。むしろ、 の を使用してvectorくださいpoint。衝突検出についても同様です。ここで 2D ベクトルを使用するのは、間違ったデータ構造です。

関数の内容に応じて、コンパイラは NRVO (つまり、戻り値の最適化という名前) を実行できます。これは、最適なケースでは、ベクトルを返すことにオーバーヘッドがないことを意味します。つまり、ベクトルがコピーされることはありません。ただし、これは、関数の戻り値を使用して新しいインスタンスを初期化し、コンパイラが関数内の実行パスをトレースして、戻りパスごとに同じオブジェクトが返されることを確認できる場合にのみ発生します。次の 2 つを検討してください。

vector<int> f(int baz) {
    vector<int> ret;
    if (baz == 42)
        ret.push_back(42);
    return ret;
}

vector<int> g(int baz) {
    if (baz == 42)
        return vector<int>(1, 42);
    else
        return vector<int>();
}

コンパイラは、 への呼び出しに対して NRVO を実行できますが、 に対しては実行できfませんg

于 2008-12-02T08:24:56.180 に答える