web-dev-qa-db-ja.com

Java:無限のゲーム世界の座標マップを格納するための優れたデータ構造とは何ですか?

私はPHPでのコーディングに慣れていますが、Javaかなり簡単な解決策ですが、どのような方法で検索しても良いサンプルコードが見つからないため、次のようになります。

私はタイルベースのマップ上でランダムに生成された2Dの無限の世界で行われるゲームをプログラミングしています(つまらない:本当に無限ではないことを知っています。世界はかなり大きいと期待しています)。 map [x] [y]多次元配列の通常のアプローチは基本的なアイデアとして始まりましたが、Javaは、=のような整数でない(つまり、負の)配列のキーシェナンガンの方法を提供しないため、 PHPは、配列キーを持つ(-x、+ x、-y、+ y)座標系を正しく持つことができません。

特定のx、y座標でタイル上のオブジェクトを検索できるだけでなく、特定のタイルの「隣接タイル」を検索できる必要があります。 (私がgetObjectAt(x、y)を取得できる場合は簡単です、私はget(x + 1、y)などを実行できます)

四分木やR木などを読みました。コンセプトはエキサイティングですが、Javaでの優れた単純なサンプル実装を見たことはありません。それに、それが私が正確に必要とするものであるかどうか、私は本当にわかりません。

アドバイスは大歓迎です

ありがとうございました

45
Aykın

私は同じ問題でこのスレッドに来ましたが、私の解決策は Map/HashMaps を使用することでしたが、これらは1次元です。

これを克服するために、マップ内のマップを使用する代わりに(面倒で非常に非効率的です)、一般的な Pair クラスを使用しました(在庫にあるものではありませんJavaライブラリー)これをPositionクラスで置き換えることができます(実質的に同じコードですが、汎用ではなく、整数または浮動小数点数)。

したがって、マップを定義するとき:_Map<Pair, Tile> tiles = new HashMap<Pair, Tile>;_

タイルオブジェクトをマップに配置するためにtiles.put(new Pair(x, y), new GrassTile());を使用し、オブジェクトを取得するためにtiles.get(new Pair(x, y));を使用しました。

[x/yは、配置したい任意の座標です(これにより、混乱することなく負の座標が可能になります!)、 "new GrassTile()"は単なるマップ作成中に特定のタイプのタイルを配置する例。明らかに-前に述べたように-Pairクラスは置き換え可能です。]

なぜあなたはあなたが尋ねるかもしれないArrayLists?なぜなら、配列リストはマッピングよりもはるかに線形であり、私の見解では、特に2次元でタイルを追加および取得するのがより難しいからです。

更新:

なぜJavaにPair()クラスがないのか疑問に思う方のために、ここに 説明 を示します。

5
Liam Gray

1)配列の代わりに、_Map<Integer, Map<Integer, Tile>>_または_Map<Point, Tile>_を使用できます。これにより、もちろん負のインデックスが許可されます

2)最初から世界の寸法がわかっている場合は、ゲッターを変更してAPIがネガを受け入れ、[線形]にそれらをポジに変換できるようにします。たとえば、世界が100x1000タイルで(-5、-100)が必要な場合、WorldMap.getTile(-5,-100)があり、_return tileArray[x+mapWidth/2][y+mapHeight/2];_は(45,400)に変換されます。

6
davin

木、四分木、二分木、赤と黒の木など、他のすべての種類の樹木は役に立ちます(巨大な森のあるマップを計画している場合を除きます)。

特殊なデータ構造には特定の用途があります。ゲームが空間インデックスを必要とする正当な理由が思いつかない限り、それを構築しないでください。典型的なシナリオが「表示領域全体を反復処理する場合、各正方形でどのタイルが表示されるかを確認する」場合、特定のキーの下に格納されている値にすばやくランダムにアクセスできる構造が必要です。そのような構造はHashMapです(PHPの使用は一種のLinkedHashMapですが、おそらく「リンクされた」部分を使用していませんでした)。

あなたはxephoxのアドバイスに従う必要があります(そして彼に信用を与えます)、そしてそれは:

  • 場所を説明するクラスを作成します(ペア、場所、ポイントなど)。
  • すべてのフィールド(おそらくxとy)をfinalにします。場所自体が変更できないことが重要です(それによって生活が非常に楽になります)。
  • 等号とハッシュコードのメソッドを生成します(IDEはそれを支援します。実装ではxとyの両方を使用する必要があります-IDE君は);
  • マップは次のようになります。Map map = new HashMap();

最良のこと:Mapインターフェースを使い続けると、ロックアウトされず、多くの改善を行うことができます。 HashMapをオブジェクトにラップして、いくつかのアルゴリズム技術を使用してマップの一部を作成するように。

3
fdreger

あなたはQuadTreeを試すことができます(ここの良い例: http://www.mikechambers.com/blog/2011/03/21/javascript-quadtree-implementation/

2
Simon

私はゲームプログラミングの専門家ではありませんが、配列に問題がなければ、座標を(-x、+ x)から(0、2x)(y軸の理想値)に単純に変換できます。

または、PHPのような連想配列に慣れている場合は、マップであるJavaの対応する構造を使用します(HashMapはOKです):Coordinateクラスを定義します。適切なequalsメソッドとhashCodeメソッドを使用し、HashMap<Coordinate>。 Coordinateを不変にすると、コードがより堅牢になり、hashCodeをキャッシュできます。

2
JB Nizet

Javaに、実験的なスペアデータ構造をいくつか書きました。

最も興味深いのは Octreap で、これは TreapOctree の間の完全に斬新なクロスであると私が信じているもので、次の機能がありました。 :

  • 60ビットの世界座標(約1,000,000 * 1,000,000 * 1,000,000グリッド)
  • サポートされている負の座標
  • 空のスペースはストレージを必要としません(非常にまばらな世界をサポートします)
  • 同一セルのボリュームを圧縮します(たとえば、同じ材料の大きなブロックは効率的に保存されます)
  • 読み取りと書き込みのO(log n)
1
mikera

マップをチャンクに分割してみてはいかがですか(そうです、Minecraftのファン、これがどこで使用されているかも知っています:P)。したがって、2つの座標系があり、どちらも同じ原点を持っています。

  • _x/y_座標
  • _c1/c2_チャンク座標

チャンクは常に固定サイズの実世界座標です(128x128など)。次に、クラスChunkがあり、すべてのピクセルのすべての情報を含む固定配列(128x128)があります。そして、チャンクを_Map<ChunkCoordinates, Chunk>_に格納します。 HashMapをお勧めします。

プレーヤーが特定の地域にいるときはいつでも、必要なチャンクがマップから読み込まれ、そこから固定サイズの配列にアクセスできます。チャンクが_x/y_座標に配置されていることがわかっている場合は、Pixel Chunk#getPixel(long x, long y)などのサポート関数を使用することもできます...

ところで:これは、本当に必要になるまで全世界の生成を延期する簡単な方法も提供します。最初は何も生成されず、マップでChunkにアクセスしてもすぐにはまだ生成されません。 、あなたはそれを単に生成することができます。または、起動が簡単な場合は、起動時に満たすこともできます。 (ただし、無限の世界を埋めるには、たとえそれが疑似無限であっても、長い時間がかかります)

1
brimborium

ハッシュマップスタイルのソリューションは、隣接関係の計算にひどいものであり、データセット全体の反復が必要です。

Quadtreeやoctreeのようなものは、それが無限ではなく、任意のサイズ(そこに違いの世界)があることを除いて、完璧です。

しかし、考えてみると、ArrayListは無限ではなく、単に任意のサイズに成長しますよね?

したがって、クアッドツリーは、「無限」のプロビジョニングが完璧であることを除いて、スパースで非常に優れた隣接計算です。必要と思われるサイズの100倍のサイズを使用しないのはなぜですか(まばらで、大したことではありません)。エッジに近いところまでたどり着いた場合は、はるかに大きな新しい四分木を割り当てます。

注意して(独自のクワッドツリーを実装する必要がある場合)、クアドツリーをほとんどアップグレードせず、コピーせずに「アップグレード」できると思います。既存のすべてのアドレスにいくつかのビット(アドレスバイナリであり、四分木は各ビットが既存のユニバースを一方または他方の半分に分割することを表します)。

0
Bill K

おそらくMapの実装を使用したいと思うでしょう。格納するデータの量とアクセスパターンに応じて、HashMap、SortedMapなど(ソートマップはシーケンシャルアクセスに非常に適しています。HashMapはランダムアクセスに適しています)。

2次元マップを使用するか、2次元の索引を1次元マップのキーに変更することができます。

0
patros

これは2つの別個の質問です。「無限」マップを作成できるように負の配列インデックスをシミュレートする方法と、タイルを効率的に格納する方法です。

最初の質問では、1つのハックは、各象限に1つずつ、4つの別々のマトリックス(マトリックス?)を維持することです。次に、すべてのインデックスが正になる可能性があります。

2番目の質問では、疎なマップが必要です。あまり効率的ではない方法の1つは、ハッシュマップのハッシュマップを持つことです。実際、これで最初の問題も解決できます。

HashMap<String, HashMap> x = new HashMap()
HashMap<String, Tile> y = new HashMap()

// get the tile at coordinates 1,2
Tile myTile = x.get("1").get("2");

// this would work as well
myTile = x.get("-1").get("-2");

整数をキーとして取り、はるかに効率的な独自のMap実装を実行できます。

0
ccleve

本当に高速でスケーラブルになりたい場合は、必ずスパースオクトリーを使用してください。

http://en.wikipedia.org/wiki/Octree

Javaでの実装については知りませんが、それは取るに足らないことです。ノードが現在リンクされているビットフィールドを1バイトに格納し、リンクリストを使用してそれらの参照を維持します(各Octreeノードに対して最大8エントリの個別のリスト)。

0
Bernd Elkemann