トップダウン2Dマップ上で画面外のオブジェクトを効率的にカリング


8

ゲームプログラミングでは効率が重要であることを知っています。以前に「マップ」をレンダリングした経験はありましたが、おそらく最善の方法ではありません。

2Dトップダウンゲームの場合:(世界のテクスチャ/タイルをレンダリングするだけです)

たとえば、1000x1000(タイルなど)のマップがあるとします。タイルがカメラのビューにない場合、レンダリングされるべきではありません-とても簡単です。表示されないタイルをレンダリングする必要はありません。しかし、マップ内に1000x1000オブジェクトがあるか、おそらくそれより少ないため、レンダリングされるかどうかを確認するためだけにすべての1000 * 1000タイルをループ処理したくない場合があります。

質問:この効率を実装する最良の方法は何ですか?それで、「素早く/より速く」、どのタイルがレンダリングされると想定されるかを決定できますか?

また、SpriteBatchでレンダリングされたタイルを中心にゲームを構築するのではないため、四角形はなく、形状はさまざまなサイズで複数のポイントを持つことができます。たとえば、10ポイントの湾曲したオブジェクトとそのシェイプ内のテクスチャです。

質問:この種のオブジェクトがカメラのビューの「内側」にあるかどうかをどのように判断しますか?

48x48の長方形で簡単です。X+ WidthまたはY + Heightがカメラのビュー内にあるかどうかを確認してください。複数のポイントで異なります。

簡単に言うと、何百万ものオブジェクトを同時に実行/ループする必要がないように、コードとデータを効率的に管理する方法です。

回答:


6

複雑なオブジェクト私は最善の方法は、周囲の長方形にそれらを削減し、その長方形がビューポート内にあるかどうかをチェックするだけだと思います。実際には表示されないテクスチャーをレンダリングする場合でも(その形状のため)、より複雑な検出アルゴリズムを実行するよりもおそらく高速です。

大きなマップを効率的に処理するには、マップをより大きなスケール、たとえば10x10で細分割する必要があります。次に、ビューポートの交差点を確認します。最悪の場合、この「リージョン」が4つヒットすると、(100x100)* 4 = 40Kオブジェクトになります。これは簡単な例です。実際の使用では、このようなサブディビジョンと衝突検出に特に効率的なクワッドツリー構造を検討する必要があります(ビューポートの可視性チェックは、基本的にはビューポートとスプライト間の衝突チェックです)。


マップタイルにクワッドツリーを使用するのは少しやりすぎです...レンダリングする必要があるタイルの適切なインデックスを計算するだけなので、さらに、リージョンはより単純なので、最適化の最初のラウンドにそれらを使用することをお勧めします。後ほど:) +1で四分木を理解して使用するのに役立ちます。
Liosan、2012年

@Liosanこの「タイル」が同じサイズであるかどうかは問題から明らかではありません。それ以外の場合、ソリューションは非常に簡単です。
Petr Abdulin、2012年

そうです、Deukalionは別の回答に対して「タイルは必ずしも正確に同じサイズであるとは限らない」とコメントしました。
Liosan、2012年

QuadTreeは正確な領域サイズ以外でも機能しますか?長方形内のオブジェクトは長方形ではないので、すべての領域は長方形のサイズであってはなりません。したがって、レンダリングされるのは、言うまでもなく1024x1024ピクセルのグリッドではなく、その形状は非常にオーソドックスなものになる可能性があります。
Deukalion

まあ、私はそうは思いません(私自身は実際にクワッドツリーを使用したことがありません)が、すべてを周囲の四角形領域に入れることができる場合は、それほど問題ではありません。とにかく、クワッドツリーが何らかの理由でタスクに適切でない場合は、おそらく、同様のアプローチを使用する必要があります。
Petr Abdulin

3

多くのモバイルオブジェクトがある場合、それらを多次元ツリー構造の座標で格納する必要があります。これにより、特定の長方形の中にあるすべてのオブジェクトのリストを効率的に取得できます。x座標またはy座標で並べ替えることもできます。これは、オブジェクトスプライトが重複する場合の描画順序に重要です。

これは、衝突検出にも非常に役立ちます。

詳細については、kdツリーに関するウィキペディアの記事を参照してください。

2Dツリーが複雑すぎる場合は、簡単ですがそれほど効果的ではない方法もあります。オブジェクトをタイルの子として保存します。オブジェクトを移動すると、古いタイルのオブジェクトリストから削除され、新しいタイルのオブジェクトリストに配置されます。オブジェクトを描画するときは、ビューポートのタイルを繰り返し処理して、オブジェクトを取得します。次に、すべてをy座標で並べ替えて描画します。


1

それが最善の方法であるかどうかはわかりませんが、これは私がそれを行う方法を学ぶ方法です:

「タイル」の2次元配列がある

public Tile tilemap[][];

そして、Vector2を使用して「カメラ」の位置を決定します。シーン内にあるものだけをレンダリングします。大きな長方形は画面に表示できるものであり、シーンの残りの部分を描画するのには役に立ちません。

次に、カメラをシーンの中心に配置したい場合は、オフセットを取得する必要があります。

offsetX = (graphics().width() / 2 - Math.round(cam.Position().X));
offsetX = Math.min(offsetX, 0);
offsetX = Math.max(offsetX, graphics().width() / 2 - mapWidth);
offsetY = (graphics().height()) / 2 - Math.round(cam.getPosition().Y);
offsetY = Math.min(offsetY, 0);
offsetY = Math.max((graphics().height() / 2 - mapHeight), offsetY);

今、配列のどの部分で表示タイルが開始および終了しますか?

firstTileX = pixelsToTiles(-offsetX);

lastTileX = firstTileX + pixelsToTiles(graphics().width());

firstTileY = pixelsToTiles(-offsetY);

lastTileY = firstTileY + pixelsToTiles(graphics().height());

int pixelsToTiles(int pixels) {
    return (int) Math.floor((float) pixels / Tile.getHeight());
}

そして、あなたのdrawメソッドでは、配列の可視部分をループするだけです:

   for (int x = firstTileX; x < lastTileX; x++) {
        for (int y = firstTileY; y < lastTileY; y++) {
              Vector2 position = new Vector2(tilesToPixelsX(x) + offsetX,
                        tilesToPixelsY(y) + offsetY);
              tilemap[x][y].Draw(surf, position);
        }
    }

1
はい、しかしタイルは物事を簡素化するための例でした。オブジェクトが既に複数のポイントを持つより高度なシェイプではなく、長方形/タイトルのシェイプで「ビュー」にあるかどうかを判断するプロセスを理解していることをすでに書いています。また、XNAの各Update()メソッドですべてのタイルを「通過しない」ようにするものを探していました。約10000のオブジェクト(3点以上の形状)を持つ「大きい」マップがある場合、これはその方法ではありません。更新ごとに、10000回の更新と同じくらい多くの計算を繰り返す必要があります。私はタイルを使用していませんが、これは効率的ではありません。そこに行ったことがある。
Deukalion

そして、タイルは必ずしも正確に同じサイズであるとは限らないので、それでそれを行うこともできません。長方形は使用しません。
Deukalion

簡単に言えば、レンダリングする必要のあるオブジェクトだけをループしたいのですが、まったくすべきでないオブジェクトをループしたくはありません。
Deukalion

@Deukalion Riktothepastのコードは、画面の境界ボックス内に表示されるタイルのみをループします(ただし、これはあまり明確ではありません)。同じ基本的なテクニックを使用して、特定の座標セット内のタイルの長方形をループできます。
DampeS8N 2012年

0

シーン全体であるが表示されないビットマップを1つ持つことができます。そして、表示された画面サイズのカメラレイヤービットマップは、シーン全体から描画するだけで、表示する必要のある部分のみを描画します。

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