「レベル文字列」の最善の解決策は?


10

レベルの開始時にランダムなレベルマップを生成するゲームがあります。レベルを保存およびロードする方法を実装したいと思います。

XMLがすべての変数を保存するのに適したオプションであるとしたら、XMLを解析してまったく同じレベルを生成できるものを簡単に作成できると思いました。

しかし、XMLはおそらく私のニーズに対しては行き過ぎです。昔、ゲームを保存する機能がなかった古いSegaコンソール(ワームズゲームでもそうだったと思います)を覚えていて、書き留めることができるたくさんのキャラクターを提供していたことを覚えています。後でそのストリングをパンチインすると、正確なレベルがロードされます。

「レベル文字列」は良いオプションでしょうか?それはある種の「base60」変換でしょうか?これをどのように実装しますか?

回答:


20

おそらく、保存する必要があるのはランダムシードだけです。これは通常、単なるintです。もう少し不透明にしたい場合は、intをbase64にエンコードできますが、おそらくそれは必要ありません。


実際の単語からシードを生成することもできます(おそらく、シードを取得するために文字を追加して購入します)。これにより、より意味のあるものを返すことができます。単語をゲームに保存するか、取得する必要があります。
ジョナサンフィショフ2010

それは問題ありませんが、ゲームがプレイされている場合は、動的データも保存する必要があります。文字列は文字の位置やスコアなどを保存する必要があります。では、この文字列を生成する最良の方法は何ですか?
アダムハート

私はおそらくJSON(または、必要に応じてXML)を使用して世界の状態をシリアル化し、その文字列をbase64エンコードします。しかし、それがすべて有用であるかどうかはわかりませんが、GUI指向の保存/読み込みシステムを作成しないのはなぜですか?これはWebベースであり、このサーバー側を処理しないことを想定しています。例としてshygypsy.com/farm/p.cgiご覧ください。
coderanger、2010

23

セーブゲームで使用する形式に関係なく、バージョン番号を入れてください。バージョン番号に分岐することにより、下位互換性のあるロードを使用できます。または、古すぎるセーブを安全に認識できます。ロードする。

そうでなければ後悔するでしょう。


7
+1は実際には質問に答えませんが、とても重要です。
ジョナサンフィショフ2010

10

JSONは優れていますが、YAMLのほうが優れています。:) http://www.yaml.org/およびhttp://code.google.com/p/yaml-cpp/より使いやすい実装の1つ。

YAMLはJSONのスーパーセットであり、いくつかの優れた機能のサポートが追加されています。

  • バイナリノード。これは、レベルの説明のために処理する可能性のある種類のデータをシリアル化するのに最適です。JSONでは、書き込み前/解析後に、Base64などの選択した中間形式に変換する必要があります。YAMLには、パーサーライブラリに代わりに実行するように指示する!! binaryノードタイプがあります。
  • ドキュメント内の参照。同じオブジェクトがドキュメントに2回出現する場合、JSONはそれを2回書き込み、再び読み取ると2つのコピーを取得します。多くのYAMLエミッターはこの状況を検出でき、2番目のコピーの代わりに、ロード時に検出できるときに最初のコピーへの参照を出力します。
  • カスタムノードタイプ。たとえば!! Player、!! Enemyなどを使用して、リスト内の各マップにフラグを付けることができるため、タイプ情報をより帯域外に保つことができます。
  • YAMLはより読みやすいフォーマットをサポートしています。
  • JSONはYAMLのサブセットであるため、ほとんどのYAMLリーダーはJSONドキュメントを問題なく読み取ることができます。

1
Yamlはとてもかっこいいです。スーパーセットの機能のいくつかを回避すれば、問題なくjsonに変換できます。
Jethro Larson

3

ゲーム内のすべてのデータをシリアル化する場合は、ファイル形式としてJSONをお勧めします。これが、XMLを使いやすく、多くの言語でサポートが非常に優れている理由です。

私はこのライブラリをC ++に使用しましたが、非常にうまく機能します。

http://jsoncpp.sourceforge.net/


3

XMLは、サイズに制限がなく、ネイティブでサポートされている場合(.NETやFlashなど)に適していますが、スリムなフォーマットが必要な場合は、独自のフォーマットとパーサーを簡単に作成できます。私は通常1文字を使用します。各オブジェクトを区切るためのカンマ。文字列をデコードするには、コンマで分割します。各オブジェクトには異なるプロパティが必要なため、これらをセミコロンなどの異なる文字で区切り、別の文字を使用してプロパティ名とプロパティ値を区切ります。結腸。したがって、string.splitを使用するだけで、正規表現なしですべてを簡単にデコードできます。次に例を示します。

id:1;x:5;y:45.2;angle:45,id:28;x:56;y:89;angle:12;health:78

プロパティ名を1文字に抑えることで、さらに多くのスペースを節約できます(健康のためのhなど)。例えば。

i:1;x:5;y:45.2;a:45,i:28;x:56;y:89;a:12;h:78

JSONの代替と比較:

{"o":[{"i":1, "x":5, "y":45.2, "a":45}, {"i":28, "x":56, "y":89, "a":12, "h":78}]}

また、数値のサイズを小さくしたい場合は、印刷可能なUTF16文字の完全なセットを使用して数値をエンコードできます。このスレッドに触発されて、画面の1つのキャラクターにどれだけのデータを詰め込めるかについてStack Overflowで質問しました。点字、漢字、チェスの駒を持っていても構わないとしたら、答えは整数で40,000以上の値のようです。♔♕♖♗♘♙♚♛♜♝♞♟

さらにサイズを小さくするには、読み取り/書き込み順序を使用して、どちらの値がどちらであるかを判別できます。最初の2文字はIDを表し、次の2文字はx位置、次の2文字はy、次に角度、そしてヘルスです。など。

F5DGP@%&002DFTK#OP1F

他の例と同じ情報をすべて保存できます。

タイルグリッドは、各文字が異なるタイプのタイルを表す文字列として格納できます。例:

i789pog5h3kl

溶岩、9は草などを意味します


これは私が求めているものに沿ったものです。私の質問ではXMLについて触れていますが、それでも人々はそれを提案しています!
Adam Harte、2010

1
その周りに{}を追加すると、基本的にJSONがあります;-)
coderanger、

引用符のロードも追加する必要があります。おそらく文字数は2倍になりますが、オブジェクトのネストが発生します。
Iain、2010

1

.Netでコーディングしている場合、XMLは非常に簡単です。レベルクラスをXMLにシリアル化/非シリアル化したり、数行でXMLにしたり、XMLからデシリアライズしたりすることができます。

TheMapはMap型の変数であり、すべてのデータが読み込まれます。

Dim TheMap As New Map

Mapクラスが既に構築されているとすると、マップはXMLに保存されます。

Dim Serializer As New System.Xml.Serialization.XmlSerializer(GetType(TheMap))
Dim Strm As New FileStream("c:\Map.xml", FileMode.Create, FileAccess.Write, FileShare.None)
Serializer.Serialize(Strm, TheMap)
Strm.Close()

これにより、XMLがマップクラスに再度読み込まれ、コードで再び使用されます。

Dim Reader As New StreamReader("map.xml")
Dim Serializer As New System.Xml.Serialization.XmlSerializer(GetType(TheMap))

TheMap = Serializer.Deserialize(Reader)
Reader.Close()

これで、XMLファイルがクラスにロードされ、簡単に使用できるようになりました。

「レベル文字列」の問題については、前に述べたようにうまくいくので、シード番号を「レベル文字列」として使用できます。

それ以外の場合は、必要に応じてさまざまなマップを事前に生成し、それらすべてを「レベル文字列」で保存し、それを使用して適切なマップを取得することができます。


XMLが嫌いなのに+1-言語がデ​​フォルトのシリアル化フォーマットを提供している場合、特に他のツールが理論的に解析できるフォーマット(XML / JSON / YAMLなど)である場合は、最初にそれを検討するのが最善です。

0

私は単純なstructまたは同様のデバイス(言語によって異なります)を使用して、すべてのゲームの状態を中央の場所に格納します。セッター/ゲッターの保護が必要な場合は、構造体をでラップできますclass

気になる場合は、ビットフィールドを利用するか、ビット単位の演算子を使用して自分でビット操作を行ってください。

一部の言語では、構造体のパディングとパッキングのルールが少し複雑になる可能性があることに注意してください。

#pragma(など#pragma pack(1))またはを使用して__attribute__、構造体を密にパックし、パディングを排除することもできます。これは、コンパイラとターゲットアーキテクチャによっては機能しない場合があります。

ビットフィールドとパックプラグマまたは属性を使用すると、移植性が低下する可能性があることに注意してください。ハードウェアアーキテクチャ全体で、構造体フィールドのエンディアン(バイト順)も変更される場合があります。したがって、移植性を重視している場合は、これを回避する必要があります。

(たとえば、パックマンの場合、この構造体には、単純にマップIDまたはマップシード、パックマンのxとyの位置、4つのゴーストxとyの位置、および32-64ペレットの有無の大きなビットフィールドが含まれます。最大値が何であれ。)

構造体を取得したら、xxencode関数などに渡します。

encode_save( char * outStringBuf, size_t outStringBufSize,
             const SaveStruct * inSaveData, size_t inSaveDataSize )

この関数の記述は少しエラーが発生しやすいです。たとえば、一度に6ビットを取得するために必要に応じてバイトをシフトおよび結合し、適切な文字に変換する必要があります。私が「おもしろい」目的でこれを行っていなかった場合を除いて、私は個人的に他の人のコードを見つけ出そうとします(そして、そのためのテストスイートがおそらく必要です)。

struct適切な場所でオールドスクールの力を過小評価しないでください。ここでは、GBAとDSのゲームに使用しました。


生の構造のシリアライゼーションは移植性がなく、非常に壊れやすいwrt新しいコードです。リソースが非常に限られているプラ​​ットフォームでデータをベイクする最後のステップでない限り、それは非常に時期尚早な最適化です。8ビットよりも6ビットを選ぶ理由はありますか?いずれにしても、この形式は人間が読める形式にはなりません。実際の構造レイアウトの速度とデバッグ可能性を利用することもできます。

@Joe:私はそれが移植不可能であり、潜在的な懸念のいくつかであることを述べました。この質問では、古いSegaゲームのような人間が読める「レベル文字列」を具体的に求め、base60変換について言及しました。構造体をxxencodeのようなものに渡すと、これが行われます。これは必ずしも時期尚早な最適化ではありません。ビットパックされていない単純な構造体は、保存データを格納するための適切な「中央」方法であり、データとやり取りするコードの多くを簡略化する場合があります。例として、非会員で友人以外の構造と「裏返し」のプログラミングに関するNoel Llopisの最近の記事を参照してください。これはただのキスです。
2010

1
私はこの方法に完全に同意します。はい、バージョン間やシステム間での移植性はありませんが、セーブゲームは、開発中に再利用したり、プラットフォーム間でコピーしたりする必要のあるリソースではありません。デバッグ可能性は、読み取り/書き込みのインプレースの問題ではほとんどありません。一度実装すれば、永久に機能します。IF拡張性が本当に問題である場合-最初のybyte / wordとしてバージョン番号を追加し、それをオンにすることができます(ただし、データの脆弱性が発生します)。XMLがバージョン管理の問題を解決するわけではありません。値を設定するだけではなく、より複雑になる傾向があります。はい、私は実用的です。
Kaj

0

XMLは、任意に構造化されたドキュメント(要素がツリーのさまざまなレベルに表示される場合があります)または外部形式の埋め込み(xhtmlページにsvgを配置するような)に適しています。これらの要件がない場合、それは本当に非効率的な形式であり、csvやjsonのようなより単純なものが望ましいです。

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