潜在的に無限の2Dマップデータを保存する方法は?


29

現在、100 x 100タイルのチャンクを処理できる2Dプラットフォーマーがあり、チャンク座標はlongとして格納されているため、これがマップの唯一の制限です(maxlong * maxlong)。エンティティの位置などはすべてチャンク関連であるため、制限はありません。

私が抱えている問題は、何千ものファイルを持たずにこれらのチャンクを保存してアクセスする方法です。すぐにすべてを開く必要のない、できればすばやく低コストのアーカイブ形式のアイデアはありますか?


2
よりインスピレーションを得るために調べることができるデータ構造には、スパース行列(マルチレベル)ページテーブルがあります。
アンドリューラッセル

低優先度:「long」データ型が32ビットか64ビットかを明確にできますか?
ランドルフリチャードソン

2
@RandolfがこれがC#であることを考えると、おそらく彼longは64ビットのC#を意味する(したがってmaxlongはInt64.MaxValue)。
アンドリューラッセル

Notchのブログには、Minecraftの無限のマップに関する興味深いことがあります。notch.tumblr.com
post

回答:


17

ゲーム用のカスタムマップ形式を作成します。思っているより簡単です。BinaryWriterクラスを使用するだけです。最初に、いくつかのintまたはuintでヘッダーを記述します。ヘッダーに含める情報:

  • あなたのファイル形式のマジックストリング/マジックナンバー。
  • このファイルに記述されているチャンクの開始/終了/サイズ

そして(そして、ここにパフォーマンスの重要な部分が来る

  • ファイル内の開始位置を記述するint。したがって、特定のチャンクを検索する必要はありません。

上記の方法を使用すると、何らかの種類の説明(ユーザーが指定した領域/チャンクの名前、または単に座標)と2番目の値としてファイル内の位置を含む、ファイルコンテンツのインデックスを作成できます(そしてする必要があります)。

次に、特定のチャンクをロードする場合は、インデックス内を検索するだけです。位置を取得したら、fileStream.Position = PositionOfChunkFromIndexを設定するだけで、ロードできます。

それは、ファイルの内容を最も効率的に説明するヘッダーを持つファイル形式の設計に関するものです。

作成したカスタム拡張子を付けてファイルを保存するだけです。

ボーナス:ファイルの特定の領域/コンテンツ全体(ヘッダーではない!!)にBZip2圧縮を追加します。これにより、特定のチャンクをファイルからアンパックして、非常に小さなメモリフットプリントを実現できます。


12
このファイルをオンザフライで変更する場合は、固定サイズまたは外部ヘッダー/インデックスのいずれかが必要になります。これにより、ファイルを書き換えずにファイルにチャンクを追加できます。ファイル全体(オフセットの変更による)。
アンドリューラッセル

その時点で、フラットファイルデータベースを実装しているだけではありませんか?
猿の稲妻

13

同様の問題にぶつかり、データを処理する独自の構造を作成することにしました。四分木に大まかに基づいていますが、すべての方向に無限(少なくともIntと同じくらい)の拡張性があります。これは、Minecraftが現在行っているように、中心点から拡大したグリッドベースのデータを処理するように設計されました。メモリのスペース効率がよく、非常に高速です。

各ノードに最小の大きさを指定することができ(7の大きさは128x128になります)、任意のノードにサブノードの指定された割合が入力されると、自動的に2次元配列に平らになります。これは、非常に密集した部分(たとえば、完全に探索された大陸)がアレイのパフォーマンス(非常に高速)を持つことを意味しますが、まばらに密集した部分(たとえば、誰かが上下にさまよいながら内陸を探索しなかった海岸線)優れたパフォーマンスと低いメモリ使用量を備えています。

私のコードはここにあります。コードは完全であり、テスト(単体テストと負荷テスト)され、完全に最適化されています。ただし、内部の仕組みはまだ十分に文書化されていませんが、すべてのパブリックメソッドは使用可能なはずです。誰かがそれを試してみることにしたなら、質問やコメントで私に連絡してください。

データをファイルに保存するためにまだ使用していませんが、これは興味深い問題であり、次に取り組むかもしれません。


これは基本的に拡張可能なツリーですよね?私は何が欠けていますか?
kaoD

2
拡張可能なツリーに対する最大の改善点は、ツリーのように構造化された状態を維持するのではなく、2D配列に大量に配置された(デフォルト70%)ツリーの特定のノードを「フラット化」することです。これにより、無限配列の(無限)サイズがなくても、配列検索の速度が得られます。
dlras2

葉ノードと内部ノードの両方ですよね?興味深いアイデアは、良い結果をもたらすかもしれません。これが必要になったら試してみます。ところで、コードと迅速な回答を提供してくれた+1。ああ、ユニットテストも行われました。私は(悲しいことに)私の個人的なプロジェクトではそうしませんでした:)
kaoD

私は仕事で単体テストを行っていないので、悲しいことにそれは私の反抗の方法です..私はそれのためにデモアプリを作成しました。数日間は見やすいので、ここにも投稿します。あなたがそれを見るとき、それははるかに理にかなっています。
dlras2

1
見失いました、ごめんなさい!まだクリーンアップしたいのですが、クラスと宿題の間でコードの一部をゆっくりと修正しているので、しばらくはそうはなりません。今のところ、古い、未かなりデモはここにある:j.mp/qIwKYtのUn-prettyが、私は部分的にそうREADMEを読んで、ここに質問をすること自由に感じたりすることを忘れないでください、それは説明の多くを必要と意味しますメールで。
dlras2

3

代わりにデータベースを使用することもできます-PostgreSQLには、X座標とY座標によって配置されるこのタイプのデータに最適化された特別なインデックス機能があります。返されるデータが正方形または長方形の領域ではなく、特定の半径内にあることを指定することもできます。

  PostgreSQL(無料でオープンソース)
  http://www.postgresql.org/

他のデータベースもあります。クライアント側では、スタンドアロン(ゲームクライアントアプリケーションで開始)を実行したり、コードライブラリの一部として含めることができるため、特定のタイプがこれに適している場合があります。 「ただ使用する」ことができます。利点は、ほとんどのSQLデータベースエンジンが既にこれを非常にうまく行っているため、インデックススキームを設計する必要がないことです。

データベースアプローチの利点は、チャンクを小さくできることです(または、チャンクを完全に取り除き、タイルを直接使用しますが、多くのタイルの少なくとも小さなチャンク/グループの使用は、設計によってはより効率的です)。次に、SQLクエリを使用して、表示可能な領域よりも大きな領域を取り込みます。近くの表示できない領域に重なるようにプリロードすることにより、プレイヤーがキャラクターを動かす前にタイルを準備できるため、より良い(できればスムーズな)ゲーム体験が得られます。

Ashen Empiresなど、一部のゲームは、最初に取得した後、ローカルハードドライブにマップデータの「キャッシュ」を保持することに気付きました(これは間違いなくネットワークI / Oを減らすためです)。

  Ashen Empires(無料でプレイ、美しい2D実装)
  http://www.ashenempires.com/

各チャンク/タイルで「最後に更新された」タイムスタンプを追跡することも役立ちます。ローカルに保存されたデータが利用できる場所では、SQLクエリに追加の「WHERE timestamp_column> $ local_timestamp」句が含まれ、更新されたチャンク/タイルのみが取得されるためですダウンロード(このように帯域幅を節約する2つの利点は、接続コストの削減と、プレーヤーのラグの減少です。これは、ゲームが人気を博したときに明らかになります)。

Ashen Empiresのスクリーンショット(数人のキャラクターが地元の銀行にいます。床の骨の外観から見ると、いくつかのスケルトンモンスターがさまよい、地元の町の警備員によって虐殺されたようです):

ここに画像の説明を入力してください


2

それらを保存およびアクセスせず、必要なランダムシードとプレイヤーのマップへの変更のみを保存します。次に、実行時に必要な部分を生成します(生成アルゴリズムを実行してから、プレーヤーの変更を適用します)。正しい一貫した生成手順を使用すると、結果のマップは常に同じ開始シードに対して同じになります。

理論的には、この方法で非常に小さなファイルに保存する文字通り無限のマップを実行できます。


@Josh Petrieは、私の投稿に対する言語および文体の大幅な修正に感謝します。残念、私は編集に賛成できない:-D
shコード

1

チャンク(あなたの世界のある種の「亜大陸/国」)を分割する方法はありますか?したがって、メモリ内にチャンクを持つためにロードする必要があるサブファイル/大きなファイルの一部をすばやく見つけることができる何らかのインデックスファイルを持つことができます...


チャンクを分割する方法は常にあります。常に。プレーヤーに表示されるか、システムの他の部分に関連するかどうかに関係なく、通常は複数の異なる方法で、ワールドデータをチャンクに分割する方法が常にあります。
shコード

0

Minecraftからアイデアを得ることができます。当初は、チャンクごとにファイルがありました。現在は、MCRegion形式を使用します。この形式では、チャンクが32x32の領域にグループ化され、ファイルごとにそれらの1つが保存されます。

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