10
\$\begingroup\$

I have a game that generates a random level map at the start of the level. I want to implement some way to save and load the level.

I was thinking maybe XML would be a good option for saving all the variable, then it would be easy for me to build something that can parse that XML and generate the exact same level.

But XML is probably overkill for my needs. I remember back in the day with the old Sega console that didn't have the ability to save your game (I think the Worms game did it too), that they would give you a bunch of character that you could write down. If you punched in that string later on, it would load the exact level.

Would a "level string" be a good option? Would it be some kind of "base60" conversion? How would I implement this?

\$\endgroup\$

8 Answers 8

20
\$\begingroup\$

Presumably all you need to save is the random seed, which is generally just an int. You could encode the int to base64 if you wanted to make it a bit more opaque, but thats probably not needed.

\$\endgroup\$
3
  • \$\begingroup\$ You could also generate the seed from a real word (perhaps buy adding up the characters to get your seed) and that way return something more meaningful. You would have to store the words in your game or retrieve them though. \$\endgroup\$ Jul 29, 2010 at 0:13
  • \$\begingroup\$ Thats fine, but it would also have to store dynamic data if the game is being played. The string would need to save character positions/score etc. So whats the best way to generate this string? \$\endgroup\$
    – Adam Harte
    Jul 29, 2010 at 0:19
  • \$\begingroup\$ I would probably use JSON (or XML if you prefer) to serialize the world state and then base64-encode that string. Not sure thats all that useful though, why not just make a more GUI-oriented save/load system? I assume that this is web based and you want to not handle this server side. Maybe look at shygypsy.com/farm/p.cgi for an example? \$\endgroup\$
    – coderanger
    Jul 29, 2010 at 0:39
24
\$\begingroup\$

Whatever format you use for your save games, for goodness sake put a version number in. You'll be able to have backwards-compatiable loads by branching on the version number, or you'll be able to safely recognise saves that are too old to load.

You'll regret it if you don't.

\$\endgroup\$
1
  • 7
    \$\begingroup\$ +1 doesn't really answer the question but so important. \$\endgroup\$ Jul 29, 2010 at 17:09
10
\$\begingroup\$

JSON is good, but YAML is better. :) http://www.yaml.org/ and http://code.google.com/p/yaml-cpp/ for one of the nicer-to-use implementations.

YAML is a superset of JSON that adds support for a few nice features, most notably:

  • Binary nodes. This is great for serializing the kind of data you might be dealing with for level descriptions. JSON requires you to translate to some intermediate format of your choice, like Base64, before writing / after parsing. YAML has a !!binary node type which tells the parser library to do it for you.
  • Intra-document references. If the same object appears twice in the document, JSON will write it twice, and when you read it back in, you'll get two copies. Many YAML emitters can detect this situation and instead of a second copy, output a reference to the first, when can be detected when loading.
  • Custom node types. You can flag each map in a list with e.g. !!Player, !!Enemy, etc., and so keep your type information more out-of-band.
  • YAML supports more readable formatting.
  • Since JSON is a subset of YAML, most YAML readers will have no trouble reading JSON documents.
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Yaml is very cool, and if you avoid some of the superset features it can be converted to json with no difficulty. \$\endgroup\$ Aug 1, 2010 at 19:37
3
\$\begingroup\$

If you want to serialize all the data in game, I would recommend JSON as your file format, it is why easier to use the XML and the support is very good across many languages.

I have used this library for C++ and it works very well.

http://jsoncpp.sourceforge.net/

\$\endgroup\$
3
\$\begingroup\$

XML is a good choice if you're not limited by size and it is supported natively (eg in .NET and Flash) but if you want a slim format you can create your own format and parser quite easy. I normally use 1 character eg. comma to seperate each object. To decode the string do a split on comma. Now each object needs different properties so seperate these with a different character eg semi colon, and use another character to seperate the property names from property vales, eg. Colon. All thus can be decoded easily without regex just by using string.split. Here is an example:

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

you can save even more space by keeping property names down to 1 character, eg h for health. Eg.

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

Compare to JSON alternative:

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

Also, if you want to get the size of your numbers down, you can encode them using the full set of printable UTF16 characters. This thread inspired me to ask a question over on Stack Overflow of how much data you could pack into one on-screen character. The answer seem to be somewhere over 40,000 values for an integer, if you don't mind having brail, Kanji and chess pieces: ♔♕♖♗♘♙♚♛♜♝♞♟

To get a further size reduction, you can use read/write order to determine which value is which, so the first two characters represent the id, the next two are the x position, the next two the y, then the angle, then health, etc. So:

F5DGP@%&002DFTK#OP1F

could store all the same information as the other examples.

Tile grids can be stored as just a string with each character representing a different type of tile eg:

i789pog5h3kl

where I might mean lava, 9 mean grass etc

\$\endgroup\$
3
  • \$\begingroup\$ This is more along the lines of what I'm asking. I mention XML in my question, but still people suggest it! \$\endgroup\$
    – Adam Harte
    Jul 29, 2010 at 20:43
  • 1
    \$\begingroup\$ Add {} around that and you basically have JSON ;-) \$\endgroup\$
    – coderanger
    Jul 31, 2010 at 0:55
  • \$\begingroup\$ You'd need to add a load of quote marks too. Would probably double number of characters, but you would get nesting of objects. \$\endgroup\$
    – Iain
    Jul 31, 2010 at 6:32
1
\$\begingroup\$

If you are coding in .Net then XML is super easy to go with, as you can serialize/deserialize your level class into/out of XML with just a couple lines, and then its all in a nicely managed class.

TheMap would be a variable of type Map that you have all of your data loaded into.

Dim TheMap As New Map

Assuming you have a Map class already built, this would save your map into 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()

This would then load that XML back into your map class, to be used again in code.

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

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

From this point on your XML file is now loaded into your class for easy use.

As for your "Level String" issue, what was stated before would work great, you could just use the Seed number as the "Level String".

Otherwise, you could just pre-generate however many different maps you wanted, and have them all saved with a "Level String" and then use that to pull up the proper map.

\$\endgroup\$
1
  • \$\begingroup\$ +1 even though I hate XML - If the language provides a default serialization format it's best to consider that first, especially if it's a format other tools could theoretically parse (like XML/JSON/YAML). \$\endgroup\$
    – user744
    Jul 29, 2010 at 16:22
0
\$\begingroup\$

I'd use a simple struct or similar device (depending on your language) to store all game state in a central place. If you want the protection of setters/getters, you can wrap the struct in a class.

If you feel up to it, make use of bitfields or simply do bit manipulation yourself using bitwise operators.

Be aware that in some languages, the rules for struct padding and packing may be a bit complicated -- but it may also not matter much for your case if you have a byte or two of padding.

You may also be able to use a #pragma (such as #pragma pack(1)) or an __attribute__ to closely pack the struct, eliminating padding. This may or may not work depending on your compiler and target architecture.

Note that use of bitfields and pack pragmas or attributes can reduce portability. Across hardware architectures, the struct field endianness (byte order) may also change. So you may want to avoid this if you're trying for portability.

(For e.g. Pac-Man, this struct might naively contain a map id or map seed, a Pac-Man x and y position, four ghost x and y positions, and a large bitfield for the presence or absence of 32-64 pellets, whatever the maximum is.)

Once you have your struct, pass it to something like an xxencode function:

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

Writing this function is a bit error-prone; you need to shift and combine bytes as necessary to get e.g. 6 bits at a time, then translate into an appropriate character. I'd personally try to hunt down someone else's code, unless I was doing this for "fun" (and I'd probably want a test suite for it).

Never underestimate the power of the old-school struct in the right places. We've used it a ton for GBA and DS games here.

\$\endgroup\$
3
  • \$\begingroup\$ Raw structure serialization is non-portable and extremely fragile wrt new code. Unless it is as the final step of baking data for a platform with very limited resources, it's very premature optimization. Is there a reason you prefer 6 bits to 8? The format isn't going to be human-readable anyway, you might as well take advantage of the speed and debuggability of a real structure layout. \$\endgroup\$
    – user744
    Jul 29, 2010 at 16:17
  • \$\begingroup\$ @Joe: I did mention that it is non-portable and some of the potential concerns. The question specifically asked for a human-readable "level string" like the old Sega games and mentioned base60 conversion; passing a struct through something like xxencode will do this. This is not necessarily a premature optimization: a simple struct, not bitpacked, is a nicely "central" way to store save data, and may simplify much of the code that interacts with the data. See e.g. Noel Llopis's recent articles on non-member non-friend structures and "inside out" programing. This is just KISS. \$\endgroup\$
    – leander
    Jul 29, 2010 at 16:24
  • 1
    \$\begingroup\$ I totally agree with this way. Yes, it's not portable across versions, nor systems, but then again save-games are not resources that need to be reused during development, or copied across platforms. Debuggability is hardly an issue with an in place read/write, implement it once and it'll work forever. IF extensibility is really an issue - add a version number as the first ybyte/word and you can switch on that (although that would introduce a data vulnerability). It's not like xml is gonna solve versioning problems - the tend to be more involved than just setting a value. Yes, I'm pragmatic. \$\endgroup\$
    – Kaj
    Jul 30, 2010 at 3:20
0
\$\begingroup\$

XML is good for arbitrarily structured documents(elements can appear in different levels of the tree) or foreign format embedding(like putting svg in an xhtml page). If you don't have those requirements it's a really inefficient format, and something more simple like csv or json is preferable.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .