Slashdot Mirror


Object-Oriented 'Save Game' Techniques?

GreyArtist asks: "I took a course in C++ a year ago in which the instructor claimed that global (file-scope or inter-file-scope) variables were antiquated and not to be used under any circumstances. I immediately thought of a counter argument that involved the method I use for saving game data. The games (and many of the other programs) I write use not only global variables, but consecutive global variables declared in their own separate module. To save the game (or user settings) to file, I simply save a single large segment of data that contains all the necessary information. How do other coders do it? Would they create a 'MyObject.savemyself()' method for every object in their game? Do they save all the game code along with the data? Either way, it seems like a horrid case of code (or data) bloat. What do you die-hard object-oriented fanatics have to say about this, and what method they would you use for saving games?"

229 comments

  1. how about.. by newr00tic · · Score: 1

    . ..something like a script, that replicates (i.e. replays) the code (like journaling fs when crashed), so that you get up to the most-recent point, which was when you pressed save...?

    .

    --
    A horse can't be sick, you know, even if he wants to.
    1. Re:how about.. by danielrose · · Score: 2, Informative

      If you are initiating a save by pressing save, you may as well save what your variables are at NOW.

      You don't need to log changes as they occur, like a journalled fs does, unless you wanted to eliminate the need to press save, and have everything save automagically. This may be impractical, depending on the number of variables and states you are saving in your log.

      --
      i hate pansy republicans
    2. Re:how about.. by Anonymous Coward · · Score: 0

      Nice for action replay.

    3. Re:how about.. by vinsci · · Score: 4, Informative

      You mean object prevalence like in Prevayler? See also here for a general presentation (intro here: Object Prevalence in C++).

      --

      Trusted Computing FAQ | Free Dawit Isaak!
    4. Re:how about.. by Anonymous Coward · · Score: 1, Interesting

      Does anyone remember "The Killing Game Show" on the Amiga etc? After dying, the game would allow you to replay your last life and interrupt and continue at any point. It was a geat idea - pity the game was too hard...

    5. Re:how about.. by HeghmoH · · Score: 2, Informative

      Interesting you should mention that. Bungie used to be a big fan of this technique, which they used in all of their Marathon and Myth games. The game basically just recorded the player's actions, and saved this record into the file when you hit save. Restore was a matter of loading the level and then replaying the save file until the end. How they managed not to break save files every time they released a new version, I will never know.

      One big advantage of this approach is that you can use the exact same code to create game films. Create a file the exact same way, but display the results when you play it back, and presto! Bungie did this for Marathon and Myth as well.

      The big disadvantage is that you have to be absolutely sure that the game will play back exactly the same way every time. As far as I know, the Aleph One open-source version of the Marathon engine has never, and probably will never, read normal Marathon saved games or films because of subtle differences in how the engines operate.

      --
      Mod down posts with a "Free Mac Mini/iPod" sig, they're spam!
    6. Re:how about.. by AltaMannen · · Score: 1

      How about a game manager that simply keeps track of where to respawn, and _optional_ save information for each 'original' object in the world? (automatically spawned objects need to be handled by their spawner) It is simple, compact and the only issue is to assign some form of identification to the objects that save their information so the correct save information can be passed onto the object that saved data.

    7. Re:how about.. by stew1 · · Score: 3, Interesting

      Yes. Many games these days are written as if they were a pure simulation. You have your initial state and you change state by applying inputs (user commands & time progression). It should be completely deterministic, so you can have two copies of the game that start with the same state, apply the same inputs, and arrive at the same final state.

      This may seem heavyweight, but, hey, processors are fast these days. Many games use scripting languages -- the "I need to write this in C using only global variables" approach of yesteryear is no longer necessary. Additionally, you get some great benefits. Chief among them is replayability and save&restore, but you can also get a relatively simple network protocol (mostly just send inputs, which are small), maintainable code, easy dead reckoning, and the ability to apply other optimizations on top of the model (e.g. lag thrusters). Also, an important insight is that you don't necessarily have to display your simulation state _exactly_. This way you can have reliable code to track the simulation and reap the benefits of such a model, and you can also use a bunch of hacks on top of the simulation to make the user experience look/feel good. That's a good way to isolate the hacks that go into any game.

      Jon

    8. Re:how about.. by Randolpho · · Score: 1
      You don't need to log changes as they occur, like a journalled fs does
      I'd say it depends on the game. Some games, chess for example, might want to keep a history of moves. As such, saving the history might allow you to rebuild state without having to save it.

      That said, it's almost always best, IMO, to save state *and* history (when you need to save history). Saving state along with history increases storage usage, but reduces load-time. It's a trade-off.
      --
      "Times have not become more violent. They have just become more televised."
      -Marilyn Manson
    9. Re:how about.. by Anonymous Coward · · Score: 0

      How about you fucking nerds shut the fuck up.

  2. Its always best... by boeserjavamann · · Score: 2, Interesting

    ... NOT to implement a saveMe() Method in every object i think, cause every object should have only a single responsiblity (SRP: http://c2.com/cgi-bin/wiki?SingleResponsibilityPri nciple) it would be best to create a SaveData-Object and SaveDataSaver-Object (stupid name, but u know what i mean :)) the saveData object as a value object and a saveDataSaver for the responsibility to save it.

    1. Re:Its always best... by jgrahn · · Score: 1
      Its always best ... NOT to implement a saveMe() Method in every object i think

      (Nitpick mode) ... because there can be hundreds of +0 orcish daggers lying around the dungeon, and writing a save method for each of them is tedious.

      You both mean classes, not objects. And I'm not sure this SRP principle is applicable here -- every object needs to be able to describe itself in some way if this is ever going to work.

    2. Re:Its always best... by boeserjavamann · · Score: 1

      ups, u are right. well, what can i say. it was early this morning :) of course i meant classes :) but why do u think SRP is not applicable here? and what do u mean with "describe itself"? Don't u think that a class should have only one responsibility (i.e. one reason to change)? the responsibility to persist the data should be encapsulated i think, so if there a change u only have one single point to change. always happy to hear a second (interesting) argument.

    3. Re:Its always best... by GreyArtist · · Score: 1

      No, I was actually thinking in terms of instantiations, not classes. The concerning feature of the MyObject.saveme() technique would that it would have to be executed for every instantiation of a class, not once per class. Code bloat is largely a concern of execution speed, not necessarily executable size.

    4. Re:Its always best... by BLAG-blast · · Score: 1
      No, I was actually thinking in terms of instantiations, not classes.

      Think in terms of classes and you'll only need to write one MyObject.saveme(). Inheritance can help you do the rest. Maybe if you have a game object that has many complex states you'll have to override the default saveme(), but do it and right this shouldn't be very often.

      The concerning feature of the MyObject.saveme() technique would that it would have to be executed for every instantiation of a class, not once per class.

      Make the saveme() inline if that big of a deal. With the correct use of iterators and inline of your saveme() method, I don't see any reason why some sort of global hack would be faster (unless your taking a game time performance hit to speed up your save time, which doesn't make much sense if you care about speed).

      Code bloat is largely a concern of execution speed, not necessarily executable size.

      You've got to save the same amount of data no matter how you implement it.

      There is one thing, in the item blurb you talk about saving one chuck of data. If you are trying something like this (psudeo code):

      int a=0,b=0,c=0,d=0,e=0,f=0;

      void* memstart = *a;
      void* memend = *f;

      //save game state
      mymemsave(memstart,memend);

      ...

      //loa d game state
      mymemload(memstart,memend);

      It would/could be faster, but there isn't any gaurantee the data will be in a contiguous address space. And if by chance they work of one compiler, they will break on another. Cross platform data exchange is out of the question as well. If you are doing this, please stop.

      Also, what type of game are working on where save time has to be so critical?

      --
      M0571y H@rml355.
    5. Re:Its always best... by GreyArtist · · Score: 1

      I never intended this thread to be a critique on the way I store the game state, but since you asked...

      ...there isn't any gaurantee the data will be in a contiguous address space. And if by chance they work o one compiler, they will break on another...

      For the sake of argument:

      struct gamestate
      {
      double gflen;
      double gfwdth;
      ...
      } my_gmstate;
      ...
      mymemsave(&my_gmstate, sizeof(gamestate));
      ....
      mymemload(&my_gmstate, sizeof(gamestate));
      ...

      Any compiler that rearranged the members of the gamestate structure would need to be scrapped in a hurry. As for cross-platform, any binary game save format has the potential for creating problems there. Am I going to complicate my code by converting binary data (which already exists) to some other format... not on your life.

      The object-oriented solution is the garrish hack here. Object classes should have no inherent duty to serialize their game state information at runtime. That should be a responsibility of the language itself (as other posts have mentioned), or some sort of holistic game object, or precomputed as a global data structure as I've shown. You are adding code, data, and object bloat when you create so many classes, all (yes, all) of which have to manage the persistance of their objects.

      I don't think anyone in this thread has read the "Where have all the cycles gone?" article (http://developers.slashdot.org/article.pl?sid=05/ 02/07/1916205&tid=185&tid=137&tid=189&tid=8) that slashdot posted just a week ago.

    6. Re:Its always best... by BLAG-blast · · Score: 1
      The object-oriented solution is the garrish hack here. Object classes should have no inherent duty to serialize their game state information at runtime. That should be a responsibility of the language itself (as other posts have mentioned), or some sort of holistic game object, or precomputed as a global data structure as I've shown. You are adding code, data, and object bloat when you create so many classes, all (yes, all) of which have to manage the persistance of their objects.

      I still don't see why it has to be global. This sounds like a poor program structure rather than some time/space saving trick. I don't see anything (too) wrong with writing the struct, just that it doesn't need to be global.

      --
      M0571y H@rml355.
    7. Re:Its always best... by Moses+Lawn · · Score: 1

      This discussion illustrates several very good principles:

      * Beware of absolutists who say "XYZ is always a bad idea"
      Yes, global variables can be a bad thing and make your code a nightmare. I've had to make changes to horribly tangled spaghetti code that relied on dozens of global variables in dozens of modules and I'm several years older because of it. However, it's virtually impossible (or at least very impractical) to write a program without any globals. You're always going to have some kind of global system state, or pointers to main windows or instance handles or whatever.

      You seem to have a pretty clean design here - I use the one-global-config-struct a lot myself, and it works very well for the class of problems where there are a lot of application config variables. It gets a little silly if you ty to scale it up to 200 members, but if that's the case, you have other design problems and should seriously look into how much of this data is truly global an how much is only used for certain specific sections of the app.

      One thing to consider - it's true that a compiler is not going to reorder fields of a struct (it's just a high-level wrapper around the underlying data layout), but different compilers *will* pack the fields differently - padding out ones that are not multiples of 4 or 8 or whatever. Make free with the #pragma pack (or whatever) directives and comment them well.

      * Saying "adding all this object-oriented behavior will bloat my app and slow it down" is just another instance of Donald Knuth's (originally from CAR Hoare) principle "Premature optimization is the root of all evil".

      Do not worry about the runtime speed, or the size of the code, required to have objects (which are the only things that know all about themselves) save their own state. The time involved to iterate through all your game objects with something like

      for each object in gGameObjects
      object -> save(savehandle)

      is so incalculably trivial that if you can even notice it, you have much bigger design problems elsewhere.


      The object-oriented solution is the garrish hack here. Object classes should have no inherent duty to serialize their game state information at runtime. That should be a responsibility of the language itself (as other posts have mentioned), or some sort of holistic game object, or precomputed as a global data structure as I've shown. You are adding code, data, and object bloat when you create so many classes, all (yes, all) of which have to manage the persistance of their objects.


      I am afraid you're dead wrong here. A good, well thought out object oriented design is rarely, if ever, a "garish hack". On the contrary - a design for something as complex as as a game that doesn't take OOP principles into account is almost bound to become a hack over time.

      Object classes have the *exact* duty to serialize themselves - like I said, they're the only entities that know how to. The language has no responsibility whatsoever here other than to provide the tools you need to accomplish this. A global game object only goes so far and doesn't scale - how do you handle thousands of characters or treasure items or whatever? What happens when you add a new kind of item to the game? Will you want to add a dozen fields to the status object every time?

      You are adding code and data, but you are not adding "bloat". You are adding functionality and making your life as a designer, coder and maintainer orders of magnitude easier. Objects manage their persistence in exactlty one place - the save() or serialize() method, most of whose work is done in a base class. Write it once, use it everywhere.

      I really think that a lot of people don't use C++ or other OO languages because they've been taught so badly, by academic purists like your professor. It also takes a big mindset change to start thinking in OO terms, and that can be scary as hell.

      The biggest roadblock, though, is attitudes like "C++ i

      --

      What if life is just a side effect of some other process and God has no idea we exist?

    8. Re:Its always best... by GreyArtist · · Score: 1

      Saying "adding all this object-oriented behavior will bloat my app and slow it down" is just another instance of Donald Knuth's (originally from CAR Hoare) principle "Premature optimization is the root of all evil".

      The topic of conversation here is not some "optimization" technique. The method of saving the game state affects how nearly every variable is declared and whether it is public or private. It affects whether every class is going to have an oddball member function whose purpose is not to manipulate the object itself, but to store its status in some sort of globally-accessible location. It affects the entire game design plan in almost every way, and I have learned over 20 years of programming that if something looks like its going to be inefficient at its task, it's also going to be complex and unwieldy to code.

      Saying that, I respect the fact that you are at home with the object-oriented methodology, and, at the very least, you gave me some additional references by which I can gauge things.

    9. Re:Its always best... by Moses+Lawn · · Score: 1

      Well, it sounds like your design is pretty much locked in. My advice in this case would be to go with your current scheme of writing out the global structure in one go. If running your app on other architectures is important, then you'll want to handle endianness. I believe Unicode uses a neat trick where they write 0x00FF into the file header, and, when they read it in, if it's negative, they have little-endian.

      Of course, you did ask specifically about OO techniques. The idea is not that you have "oddball" member functions - it's that an object knows how to deal with itself. At the most basic level, OO is all about data and functions that operate on that data. It's really just abstraction and information hiding, which are the basic concepts of structured programming, except instead of saying foo(&data), you say data -> foo() (okay, it's a little more complicated than that).

      I would say, though, that if a secondary feature like saving the game is having this much of an effect on your design, perhaps you might want to look at why this is, and consider a different sort of design, even if it means performing major surgery. I think you'll find that it makes your code a lot cleaner and easier to modify and extend in the long run.

      I was sort of going off on a bit of a tangent with my comment about premature optimization, but it was speaking to the point of view that always crops up in threads like this that anything other than raw C code is "bloated" and inefficient. And you did mention "horrid bloat" in your original posting. Anyway, my point was that picking your initial design (or language) specifically to avoid bloat is trying to optimize before you've even started.

      I guess the main point that someone could take from this ramble is that it's really hard to mix procedural and OO techniques in the same design, and that it's especially hard to mix them after the design is already in place.

      Do try an OO design sometime - maybe for your next (small) project. It takes a different mindset, and your design has to be *really* worked out *before* you start coding (trust me), but you might be pleasantly surprised by how well it works out. Good luck however you wind up doing it.

      --

      What if life is just a side effect of some other process and God has no idea we exist?

    10. Re:Its always best... by phats+garage · · Score: 1
      Any compiler that rearranged the members of the gamestate structure would need to be scrapped in a hurry. As for cross-platform, any binary game save format has the potential for creating problems there. Am I going to complicate my code by converting binary data (which already exists) to some other format... not on your life.

      Is this for real? Can somebody speak for this?

      From my limitted knowledge, machine code not being able to align variables on word boundaries for instance makes for some fairly severe performance hits. Do you really count on your structs (and objects) to serially line up the individual members exactly without aligning on various boundaries?

      I'm currently doing a very small records management routine and I'm specifically writing serialization for ints and other stuff within each struct (object) to avoid this very issue. I thought it was a given that you just can't memcpy a struct (object) and assume its going to be cross platform, not to mention edianedness.

    11. Re:Its always best... by GreyArtist · · Score: 1

      I think you misunderstand something in the quote block you inserted, cause I'm not sure what the question is. No, nothing is going to be cross-platform on the struct memory block copy. Byte orders might be wrong, structure packing might be different, etc. You especially wouldn't want to do it on an object, because then your function pointers would be stored as well, which is useless at best and more than likely hazardous.

  3. They're called singletons now by mrami · · Score: 5, Interesting

    :) You make a singleton called "Prefs" and each module stores its values in a map under its own key (and in GCC, the key could be as easy as __PRETTY_FUNCTION__)

    1. Re:They're called singletons now by Mariani · · Score: 2, Insightful

      Both ways of working have the same result in the end. They both make fixed references to a fixed place in the memory (the singleton's instance is also a static variable accessible through a static method).

      What benefit is there to get from storing prefs inside a singleton instance instead of making them accessible through static methods or variables? Other than cleaner code and applying the OO paradigm I can't think of any. Are there?

    2. Re:They're called singletons now by etedronai · · Score: 2, Interesting

      A singleton is really just a way to get around the way that constructors work in OO - namely that they always generate an object before any of the user code is even invoked. This does not allow for intelligent instantiation of objects and object reuse. The way that you are talking about using it is as a way to simulate global state - which is really no better than just using a global variable in the first place.

    3. Re:They're called singletons now by arkanes · · Score: 2, Insightful

      All the general advantages of OO. Encapsulation - all of your access to the global data goes through one object and in one manner. This makes extending behavior (for example, adding journaling) much simpler as well as drastically reducing the chance of errors. The object provides a namespace wrapping around the variables to prevent pollution of the global namespace. And don't underestimate the benefit of cleaner code, which is easier to maintain and easier to fix.

    4. Re:They're called singletons now by WaterBreath · · Score: 1

      In addition to the things that arkanes lists, there is also the fact that in C++ a properly coded reference-counting Singleton does not need to take up program memory except when you are using it. One static method is used to grab the instance of the class and another to free it. When all references are freed, the Singleton can clean up its instance and not take up memory. This allows your program data to remain properly modularized without also keeping a conslidated copy except during the actual process of saving or loading.

    5. Re:They're called singletons now by anti-trojan · · Score: 1

      I don't know about C++ but in Java, static methods are not thread-safe, while singleton methods can be made thread-safe.

  4. Uhhh.. by QuantumG · · Score: 2, Interesting

    Preferably your language has persistence built in. Obviously if it doesn't, *cough* C++ *cough*, you can't do that. In such situations I believe the Memo GoF pattern would be appropriate.

    --
    How we know is more important than what we know.
    1. Re:Uhhh.. by Anonymous Coward · · Score: 2, Informative

      You mean the memento pattern, right?

    2. Re:Uhhh.. by Anonymous Coward · · Score: 0

      No, then he'd have to reverse everything he loads, and he wouldn't be able to save.

    3. Re:Uhhh.. by Anonymous Coward · · Score: 0

      Maybe the mentos would work?

  5. Compressed variable dump by Anonymous Coward · · Score: 2, Insightful

    Just dump the variables to a file, compress/encrypt it (encryption only if you want to give savegame editors a hard time. simple XOR style stuff would do).

    Of course, you would want this dump to be parsable so that the game would know what variable was what, what object it was attached to, ect.

  6. use state machines and context objects by jamsho · · Score: 3, Interesting

    Globals are simply a first order abstraction when it comes to storing a program's state.

    They get unwieldly fairly fast - as soon as you start hitting any complexity.

    Try state machines (see GOF) and lots of singleton classes with 'Context' in the name.

    The state machines and context objects can save their own state as the change - and read the state back in as necessary. They can save their state to an in-memory object or straight to a database whether it be a file or otherwise. A 'load' would just work the other way...

    Just how I'd approach it.... (it and about anything else non-trivial ....)

    1. Re:use state machines and context objects by Anonymous Coward · · Score: 0

      The word is 'unwieldy.'

    2. Re:use state machines and context objects by shufler · · Score: 1

      Globals are simply a first order abstraction when it comes to storing a program's state.

      They get unwieldly fairly fast - as soon as you start hitting any complexity.


      Yes. That, and the fact that the trouble with Globals, is that anything can use it, and modules (be it procedures, functions, methods, objects, and whatever the else the word of the day is) access variables that aren't declared, making your code an annoyance to use elsewhere -- instead of copying a single module, you now have to copy the module, plus all the global declarations, and if you're not the original author (and sometimes even if you are), you may forget to copy the global variables.

      The proper way, that I have always found to work, is to make sure your function (method, etc) is passed the variables it requires to operate with. This is certainly easier said than done, but in the long run, it's a much better way than having global variables hanging about. Also, it shows others that you clearly have a grasp on what is needed and unneeded information. I can't begin to explain how many times I've found a global variable defined, with one one use of it in the entire program (or two uses, one in a method, and another in a method called by the first).

      You can pass by reference, or pass by value depending if you need to change the variable.

      I just realised that variable can also imply object, or any other sort of passable data.

  7. Serialize the objects in question... by stoborrobots · · Score: 3, Interesting
    Just implement java.io.Serializable

    From the guide:
    Object Serialization supports the encoding of objects, and the objects reachable from them, into a stream of bytes; and it supports the complementary reconstruction of the object graph from the stream. Serialization is used for lightweight persistence ...


    And serialization has been available from java 1.1 at least...
    1. Re:Serialize the objects in question... by Anonymous Coward · · Score: 2, Insightful

      Unfortunately, the OP does not have one single object that they can serialize, and they surely don't want to serialise the whole content of the memory...

      IMHO, it's a design issue rather than a language issue, if you need to keep track of independent/unrelated variables generously spread all over your code, well... you get what you deserve.

      A bit of refactoring seems to be needed, after which the OP can choose from many sound solutions, such as serialising an object to disk.

    2. Re:Serialize the objects in question... by yasth · · Score: 2, Interesting
      You generally do have something like GameMap or MobileObjectsHashTable though. The cool thing about serializable is that it ripples through all serializable things. so you could litteraly do
      ObjectOutputStream oos = new ObjectOutputStream(file);
      oos.writeObject("GameMa p");
      Of course you have to be careful that you don't implent serializable where you don't want it. And you have to have some relinking code to relink evertyhing with the GUI, but it isn't that hard.
      --
      I'd do something interesting, but my server can't handle a slashdotting.
    3. Re:Serialize the objects in question... by fforw · · Score: 1
      Unfortunately, the OP does not have one single object that they can serialize, and they surely don't want to serialise the whole content of the memory...
      You can change serialization behaviour with various means to only store exactly that what needs to be stored.

      From javadoc of java.io.Serializable :

      [...]

      Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:

      private void writeObject(java.io.ObjectOutputStream out)
      throws IOException
      private void readObject(java.io.ObjectInputStream in)
      throws IOException, ClassNotFoundException;
      [...]

      Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:

      ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
      [...]

      Classes that need to designate a replacement when an instance of it is read from the stream should implement this special method with the exact signature.

      ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
      This does not make it a language issue but it surely helps not to reinvent the wheel with every project.
      --
      while (!asleep()) sheep++
    4. Re:Serialize the objects in question... by arkanes · · Score: 1
      Unfortunately, the OP does not have one single object that they can serialize, and they surely don't want to serialise the whole content of the memory...

      Then he should have one. Encapsulate the (global) game state into an object. OP says he uses global vars for this stuff. That's fine for simple cases but it rapidly gets unweildy as complexity grows. If it helps, think of the GameState object as a namespace, and think about how that can reduce the chances of mistakes and errors. If you embrace the OO design methods, then you start seeing other things you can do, like adding support for journalling of game state (easy when all access to the game state is encapsulated in an object, much harder when it's done directly via global vars).

    5. Re:Serialize the objects in question... by dubl-u · · Score: 1

      This is a good start, but there are a couple of ways to improve.

      First, Java's serialization format is pretty opaque. That makes debugging, testing, and schema migration a pain in the ass. A better choice is XML via XStream. If you have a good object model, its output XML is pretty readable. And you can improve that by adding custom adapters for particular objects. And for the XML-is-too-verbose crowd, let me suggest putting a GZIPOutputStream in the chain.

      You generally do have something like GameMap or MobileObjectsHashTable though.

      This is a good approach in Perl, but in Java you can do better. When I see classes with names like that, it generally means the programmer is thinking too much about implementation and not enough about design. Instead of GameMap, it should be Game. Instead of passing in string keys and casting objects when you (hopefully) get the right ones out, take advantage of the language's static typing and put all that work inside of the Game object.

      For more on this topic, read the excellent book Domain-Driven Design.

    6. Re:Serialize the objects in question... by bokmann · · Score: 2, Informative

      First of all, the guy is talking C++, not Java.

      Second of all, serialization in Java is not as simple as just saying 'implements Serializable'... In some cases it can be this simple, but if you are doing anything with controlled object construction, such as singletons or the typesafe enum pattern, or if your objects hold onto things that don't make sense to be persisted (such as network connections, open files, etc), then you are going to have to do some special things.

      Take a look at the ReadResolve and WriteReplace methods, the Externalizable interface, and for Gods sake, Read the relevant parts of the book 'Effective Java' by Josh Bloch before doing anything with serialization.

  8. Actually... by orangesquid · · Score: 2, Funny

    The proper way to do this is:
    int main(int argc, char *argv[])
    {
    Game *theGame = new Game;
    theGame->setup(argc, argv);
    int retval = theGame->run();
    delete theGame;
    return retval;
    }

    Then, theGame can have private variables that are effectively global variables ;)

    --
    --TheOrangeSquid Is it any wonder things seem so awry? We swim in a sea of confusion and don't have to think to survive
    1. Re:Actually... by Anonymous Coward · · Score: 0

      And when the user doesn't allot/allow enough memory for your game,
      your game crashes when |new Game| throws an exception.

      Or if you disable exceptions then |new Game| returns null and then you crash at |theGame->setup(...)|).

  9. eh? mygame.savemethod()?? by torpor · · Score: 5, Informative

    Would they create a 'MyObject.savemyself()' method for every object in their game?

    isn't the purpose of 'object oriented programming' that you don't have to think like this? you just call the one big 'Game Object' save method, and .. all other derived/related objects, do their big save?

    seems a bit wonky to me.

    "game saves" is not just a game problem, of course. there are many, many parallels in other types of application .. embedded data loggers, for example, getting a warning that the shack is about to flood for winter, need to save their state too ..

    for me, the 'global context save and restore' is a 'built-in' to the design. i'm rather fond of libs and services which provide persistence natively .. though i think that some would argue that mmap's to flash RAM are cheating ... ;)

    --
    ; -- the corruption of government starts with its secrets. a truly free people keep no secrets. --
  10. Single instance by Bluelive · · Score: 5, Insightful

    This only makes sense if you have a single instance. (ie. Singleton) class. Sounds like your a C user lost in OO land

    1. Re:Single instance by Anonymous Coward · · Score: 4, Funny
      Sounds like your a C user lost in OO land

      Yes my am.

    2. Re:Single instance by self+assembled+struc · · Score: 1

      sounds like "you're" a non-english speaker stuck in english-speaking land.

      you're = you are.

      your = you own (possessive)

    3. Re:Single instance by jericho4.0 · · Score: 1
      you = anal.

      --
      "A language that doesn't affect the way you think about programming, is not worth knowing" - Alan Perlis
  11. Re:eh? mygame.savemethod()?? by zbyte64 · · Score: 2, Interesting

    This is a bit different... but at archspace we use a sort of db cache system. It involves many of the same problems, just at a larger scale. We cache the db into mem, generate values based off the db etc. Without getting into too much detail, every object inherits a Store class. When a db related variable is changed it checks if its already appended for update, if not, it puts itself in a update stack (sometimes it is a better idea to divide the update stack by object type because this is obviously a threaded game). Every 5 mins the updates are executed. The parent reminds me a bit of this since its all divided up. (its a tad more complex though)

  12. easy by DrSkwid · · Score: 5, Funny

    just ignore the instructor

    there are no rules !

    --
    There are places where the networks are not touching,and there are places where they are-Boeing's Lori Gunter
    1. Re:easy by AndroidCat · · Score: 3, Insightful
      Dear Miss Manners:
      My home economics teacher says that one must never place one's elbows on the table. However, I have read that one elbow, in between courses, is all right. Which is correct?

      Gentle Reader:
      For the purpose of answering examinations in your home economics class, your teacher is correct. Catching on to this principle of education may be of even greater importance to you now than learning correct current table manners, vital as Miss Manners believes that is.

      There are rules, written or not, and they have no pity.
      --
      One line blog. I hear that they're called Twitters now.
    2. Re:easy by Anonymous Coward · · Score: 0

      "Shut up", he explained.

      -- Ring Lardner

    3. Re:easy by brandonY · · Score: 1

      There once was a master programmer who wrote unstructured programs. A novice programmer, seeking to imitate him, also began to write unstructured programs. When the novice asked the master to evaluate his progress, the master criticized him for writing unstructured programs, saying, ``What is appropriate for the master is not appropriate for the novice. You must understand the Tao before transcending structure.'' From the Tao of Programming, by Geoffrey James.

    4. Re:easy by L-Wave · · Score: 1

      Do you think that's air you're breathing now? - Morpheous

      --
      I SURVIVED THE GREAT SLASHDOT BLACKOUT OF 2002!
  13. there are some objects... by Anonymous Coward · · Score: 0
    ...that have some things that need to be saved and some things that don't. Those objects that need to have something saved should implement a Save interface.

    In any case, what you need to do is read a good design/patterns book and see which one looks like a solution or even interesting.

    When you're finished with it. Give it to your teacher so they can read it too and maybe, god forbid, learn something.

    Ciao

  14. I don't get it by Anonymous Coward · · Score: 0

    To save the game (or user settings) to file, I simply save a single large segment of data that contains all the necessary information.

    "To do foo, I do foo."

    Seriously, I don't see how global variables help you with this. It sounds like you learned your own way of programming with a few odd practices, and now reject better design because you think you know better. Try paying more attention with an open mind.

  15. Object-based approach... by genneth · · Score: 3, Informative

    I'm not a CompSci student -- so I don't know the strict definitions of things, but I think this, below, counts as more of an object based approach as opposed to true OOP.

    The basic idea is that the thing you're trying to do, ie. have saved game state, ought be a first class thing. So have a global singleton that manages this, and have objects register themselves to that class, then most of the boilerplate can be collected in the global object.

    In C++, a better approach would be something like that taken by Boost.Serialization, which provide a template (STL style) framework, so that you can plug in different ways to marshal data as well as different output formats, etc.

  16. Passcodes by FLAGGR · · Score: 4, Funny

    Screw save games, just give the user a password to get back to the level. Simple. (Just kidding)

    1. Re:Passcodes by FinestLittleSpace · · Score: 1

      cor... i remember working through lemmings on the amstrad years ago, writing down each and every level password. I had piles of A4 sheets listing them... That game was SO hard by the end.

    2. Re:Passcodes by CableModemSniper · · Score: 1

      Yeah, except make the password a "hash" (not really a hash because it has to be reversable) of the game state. This way its never the same! 160bit save file yeah!

      --
      Why not fork?
  17. whatever works for you by jeif1k · · Score: 2, Insightful

    Don't change something just because someone tells you it's not the latest and greatest way of doing things. If saving the way you do works for you, just stick with it. It has a number of potential problems (portability, maintainability, version and architecture dependence), but if those don't bother you right now, there is no need to change until they do.

    1. Re:whatever works for you by Anonymous Coward · · Score: 1, Insightful

      Presumably he took this course to learn how to program better. Being handed new techniques and responding by telling the lecturer that he is wrong and carrying on doing whatever it is he is doing is not a good way of learning how to program better.

    2. Re:whatever works for you by arkanes · · Score: 3, Insightful

      This is the largest load of crap I've heard in a long time, especially directed to someone who's a student. The whole point of being a student is that you learn something, not that you just do whatever until you run into all the problems. Sure, what he has works. But he wants to know if there's a better way. He's been introduced to something that he doesn't understand, and doesn't see how it can make his programs better. So he's asking for some examples. This makes him an intelligent person (unless he's just got an axe to grind against OO and is trying to troll, but I'm assuming good faith and even if he is, this is a lousy example to pick), as opposed to being a reactionary turd who'll refuse to better himself.

  18. Re:eh? mygame.savemethod()?? by torpor · · Score: 1

    thats an interesting technique .. and its also interesting to note the different techniques for persistence that /.'ers are describing. seems like there's more than one way to stick around ..

    --
    ; -- the corruption of government starts with its secrets. a truly free people keep no secrets. --
  19. "Serialize" by Ninja+Programmer · · Score: 4, Interesting

    You simply have to model the the essential game state variables, then create a method for "serializing" them into something that can be thrown out to disk. There is no need at all for these variables to be global, just make sure you pass a "game context" down the call stack to any function which can modify the game state.

    The reason why its important to have this abstraction, is that its required in order to make in-game demos, and to have any hope of writing a networked version of your game. It can also let you do strange things like split screen the game and let two people play independent games if you like (a speed contest, for example.)

    1. Re:"Serialize" by AndroidCat · · Score: 2, Interesting
      But if you're serializing two games, never cross the streams! (Total protonic reversal!)

      But seriously, when everything serializes, stuff like game file versions or encrypting the stream on the way through get a lot easier. What happens with the block'o'memory save method when you have to insert one damned int in the middle? Toss all those old saved games?

      --
      One line blog. I hear that they're called Twitters now.
    2. Re:"Serialize" by miu · · Score: 2, Funny
      Toss all those old saved games?

      Oooh, aren't we fancy Mister "toss all those old saved games". The proper answer according to the programmers of many of the games I've played is to do nothing, blindly load the now badly formated chunk of memory, and then crash the game trying to use it. The user will eventually figure out the problem, so make sure to wipe out their preferences in one of the crashes - to give em something to remember you by.

      --

      [Set Cain on fire and steal his lute.]
    3. Re:"Serialize" by Anonymous Coward · · Score: 0

      What happens with the block'o'memory save method when you have to insert one damned int in the middle? Toss all those old saved games?

      Allow me to introduce you to a trick called versioning...

    4. Re:"Serialize" by drinkypoo · · Score: 1

      If you're talking about a console game, it's probably not going to change. If you're talking about a PC game, why not use a structured file format for your save games? There is no way to keep the user from tampering with it, so why not just make it straightforward? You can afford fairly large save games on PC platforms.

      --
      "You're right," Fisheye says. "I should have set it on 'whip' or 'chop.'"
    5. Re:"Serialize" by Telastyn · · Score: 1

      Indeed. While the whole "no globals" mantra is getting a little silly, this is not exactly the proper counter argument.

      Being able to serialize/unserialize data to a common format is pretty much required for any savegame processes and network communication. [okay okay, not required, but it makes life tons easier]

    6. Re:"Serialize" by AndroidCat · · Score: 1

      Well, when I said toss, I meant toss it at the processor and see what happens naturally.

      --
      One line blog. I hear that they're called Twitters now.
    7. Re:"Serialize" by AndroidCat · · Score: 1

      Gosh, really? Of course, you then end up with a bunch of parallel (or mixed) code to load in different versions, and it's isolated from the object code that you're initializing, but that's just me.

      --
      One line blog. I hear that they're called Twitters now.
    8. Re:"Serialize" by Anonymous Coward · · Score: 0

      the more fun version is to let the user exploit your protected xbox to gain root access based on your crummy game. :)

  20. Configuration Object by sporty · · Score: 3, Insightful

    Just use a configuration object. I would argue NOT to use a singleton. Come the day you need to migrate away from the singleton pattern, you will have a bit of work. Why migrate away? What if you wished to work with two configuration files at once? I know, it's not normal for all applications, but I've had an instance where one was the old config and one was the new. Short version, config object, no singleton.

    -s

    --

    -
    ping -f 255.255.255.255 # if only

    1. Re:Configuration Object by WaterBreath · · Score: 1

      Could you explain the situation a bit more? I'm not clear on what scenario you're describing, and why a Singleton is not appropriate for the scenario.

    2. Re:Configuration Object by dubl-u · · Score: 1

      Short version, config object, no singleton.

      Amen. About 95% of the time I see somebody using a Singleton pattern, it's a very fancy way to have global variables.

    3. Re:Configuration Object by sporty · · Score: 1
      Sure. A system I am working on allows for multiple people to log into it in tandem. It is convenient to have configuration files for each user. I *could* start a second JVM and let life happen with singletons. To save headaches, and simplicity, I have one class that servers as the kernel for this system and a configuration file object that works with that kernel class and classes it works with.


      In short, now I can have two, three, four copies of my softare running, each with a different config file, each with a different config object.

      --

      -
      ping -f 255.255.255.255 # if only

    4. Re:Configuration Object by WaterBreath · · Score: 1

      Okay, I see. That makes perfect sense. Obviously a Singleton isn't ideal for every "save-data" situation. But even in your situation, I could imagine a Singleton might be used to manage the config objects, rather than directly storing the config data. Tell the Singleton which user's data you need and it comes back with the config object for that user.

    5. Re:Configuration Object by sporty · · Score: 1

      Then my singleton would be just some static (java) method that just reads in a configuraiton object or writes it out. That's not the singleton pattern. Also, having a class that manages doing one thing with the object and prevents me from seeing the object as I should prevents me from doing dynamic things with it, like cloning, or writing it to other data sources. Singletons are good for two things. Globals and single ever existing things (Boolean.True.getInstance(), Boolean.False.getInstance()....)

      --

      -
      ping -f 255.255.255.255 # if only

  21. Beware of Memory Dumps. by Jason+Pollock · · Score: 4, Informative

    If you use a memory dump save (as it sounds to me) you will eventually notice several things:

    1) The files aren't easily loaded between versions of the software.
    2) The files aren't platform independent.
    3) The files are very fragile, and very dependent on compiler options.

    This is one of the complaints about Word document files - they can contain memory dumps. :)

    However, for simple ease of implementation, nothing beats getting a pointer and writing a block of memory to disk.

    Jason

    1. Re:Beware of Memory Dumps. by Scorchio · · Score: 2, Insightful

      Yep, in the long run, these problems will cost you more time than the quick and easy implementation saves you.

      A couple of rules of thumb I keep in mind when designing a save-game system:

      - Maintain backwards compatibility when possible. Assign unique IDs to all values stored in the file, so you're not relying so much on order or assuming the existance of something. Also, testers get pissed when their 8-hour saved game no longer works with each and every new build.
      - Add a version number, so that on the occasion that backwards compatibility cannot be maintained, you can prevent the attempted load of an old file. Programmers get pissed after spending hours tracking a bug, only to find it was due to an old incompatible saved game file.

      A little extra work now can save a lot of effort later!

    2. Re:Beware of Memory Dumps. by DimGeo · · Score: 1

      The parent is perfectly right. Just forget about memory dumps. If you insist on saving binary data (which is preferable to text data, because it saves loading and saving time), make sure you treat your binary data as a simple stream of bytes. That is, if you need to save anything larger than a byte at once, make sure you use shifts, and etc. math operations to decompose the integer, that will guarantee that the file will be cleanly read on a different architecture such as the macs (where endian-nes might have caused trouble otherwise). If you keep a tree-like structure of objects or some other data system of objects, just make sure you recurse all of them while saving. Maybe you will need some way to recorgnize objects, so that you can link them together at load time, you may use some kind of id-ing system, starting from 0 at each saving, and assigning ids to every object that goes to the hard disk. I think it is possible, but requires more thought. Then again, do not try to create something universal. Just save all the data you need to reconstruct the game state, and do not try to simply dump things. This will possibly save hard disk space (nothing more annoying than a 20 mb savegame file, which otherwise compresses to mere 100 kb - especially when the user likes to save often (like I do)).

  22. Global C++ objects not very portable by Bilzmoude · · Score: 1

    It must be noted that you loose some portability when creating global objects. Specifically, when you have objects in global space, the constructor must be called by some init code. This does not automatically get called in every environment... especially when the global object is located in a shared object.

    Unfortunately, this problem also exists when you include static objects in classes... since these objects exist in global space as well.

    1. Re:Global C++ objects not very portable by arkanes · · Score: 1

      A C++ environment which does not call constructors on global objects is non-standard and broken. This is sort of like saying that you shouldn't use malloc in C programs because you can't rely on it working. If you are in such an environment, my condolences, and I suggest to talk to your vendors and ask them why they shipped such a non-conforming pile of crap. And then stop using C++, because your tools don't work.

    2. Re:Global C++ objects not very portable by Bilzmoude · · Score: 1

      The environment that I think of is either AIX or HPUX with the vendor compilers (I dont remember which one), and it happens in shared objects. Either case, the vendor is a pretty big one, and a user such as myself cannot pull weight. B

    3. Re:Global C++ objects not very portable by arkanes · · Score: 1

      Dunno about AIX, but the HPUX vendor supplied compiler has long been known to be crap. If you're really forced to use it instead of GCC, my sympathies.

    4. Re:Global C++ objects not very portable by Anonymous Coward · · Score: 0

      you loose some portability

      "lose".

  23. Serialize by mwvdlee · · Score: 1

    It's been said a number of times already and I wholeheartedly agree; serialize.

    In short; make every class which should save it's data inherit two methods from a custom class.
    One of the methods should write the data for that particular object in the format in which it should be exported (text, xml, binary). The other method should be able to read that data again.
    Most likely you'll need to use some sort of structured fileformat as it would be simplest if the file format reflects the tree structure of objects in your game, but such things will explain themselves if you try to implement it.
    When all is done you should be able to call the either method at the trunk of your tree to parse or write all underlying objects automagically, then just save the returned data to file and you're done.

    --
    Slashdot social media options: AIM, ICQ, Yahoo, Jabber and Mobile Text. Why no MySpace?
    1. Re:Serialize by mwvdlee · · Score: 1

      As I read it, the saveme()/loadme() would handling file access itself, you wouldn't want to have that stuff at that level. The methods would basically be a setter and a getter.

      The non-OO approach would require knowledge about the the inside of the classes to be present outside the class and would be burdonsome when implementing new classes or changing old classes.

      --
      Slashdot social media options: AIM, ICQ, Yahoo, Jabber and Mobile Text. Why no MySpace?
    2. Re:Serialize by Anonymous Coward · · Score: 0

      should save it's data

      "its".

    3. Re:Serialize by BinLadenMyHero · · Score: 0
      • As I read it, the saveme()/loadme() would handling file access itself

      Why should the non-OO functions be any different? There can be functions that provide that higher-level interface, just the same.
      • The methods would basically be a setter and a getter.

      bool myobject_save(save_struct* buf); // for each object type
      bool save_object(save_struct* buf); // one for all objects
      Just like the functions for each object on the non-OO approach. It's just a matter of names. The right organization and abstraction can be done with or without OO.
      • The non-OO approach would require knowledge about the the inside of the classes to be present outside the class and would be burdonsome when implementing new classes or changing old classes.

      Yeah, there are some cases that the OOP semantics are handy. The trouble is that the OO people think everything have to be done that way, and when the OO model does not fit, it's just an extra level of bureoucracy.
    4. Re:Serialize by mwvdlee · · Score: 1

      I don't think everything has to be OOP. Hell, I still do more than half my work in plain C :)

      I just think a lot of people do not understand the benefits of having an OO model. Even when programming in a non-OO language, having OO design can help organize code in a more logical way.

      p.s. How would you plan to deal with changing tree structures with all the functions? I mean where parent-child relations can change. Would that be encoded in some difficult way in the structures passed to the different functions? And would you have to modify the huge switch() statement everytime a class was added or removed? How about organizing this in code?

      --
      Slashdot social media options: AIM, ICQ, Yahoo, Jabber and Mobile Text. Why no MySpace?
    5. Re:Serialize by BinLadenMyHero · · Score: 0, Troll

      Great.
      Here I am having a on-topic relevant conversation and being modded down just 'cause they don't agree with my point of view.
      At least now they can give a reason and mod me offtopic!
      Go on, I have karma to burn..

    6. Re:Serialize by Anonymous Coward · · Score: 0

      maybe they are modding your user name?

  24. In this case... by dmayle · · Score: 2, Insightful

    In the case you mentioned, with each module having global save state, what you might prefer to do would be to create a GameState base class, mostly virtual, with static methods for registering into a list of modules, and for iterating that list to actually save that data.

    In each module specific subclass, you implement the necessary storage, interfaces for the module, and the virtuals for actually performing the save or load.

    With proper helper functions, you can save yourself some code, and avaid any namespace issues. Plus, you'll have a framework that will be easily reusable for the next game you write, rather than having to write it all from scratch again.

  25. Serialization by SteveX · · Score: 5, Informative

    It's called serialization, and most OO frameworks support it in some way or another.

    Usually it's a way for an object to render itself to a stream, and reconstitute itself from a stream.

    That way you can save the objects to disk, or send them over the network, or whatever else you need to do with them.

    Every object serializes itself, and all of it's immediate children. Once every object does this, you can save the whole tree of objects with one call.

    1. Re:Serialization by turnerjh · · Score: 1

      Don't rely on naive serialization. Don't rely on some magical infrastructure doing all the hard work for you. Why? Because there are subtleties involved.

      Ever play a game that had bugs, and they made a patch to fix it but the patch made your old savegames not work anymore? Serialization at work, in one form or another.

      Objects change over time. Don't back yourself into a wall by requiring nonchanging datastructures in order to read old saved files. Then you get to a point where you have to destroy saved files or not add a feature/bug fix.

      Serialization is cute and sometimes works, but you should know its limitations.

    2. Re:Serialization by GoRK · · Score: 1

      And strenghts!

      Isn't the PSO hack for gamecube based on this flaw? And the other one for the XBox (Forgot what game).. Without lazy programmers, it would have taken far longer to crack these machines!

    3. Re:Serialization by SteveX · · Score: 1

      Serialization, like any other technique, can be implemented badly... Doing an fwrite(my_global_data_structure) is going to break too when you patch the application.

      When you serialize, usually the start of the stream has a version number. When you deserialize, you can decide based on the version of the stream you're reading, how to deserialize it.

      So if you add a new field to an object, when you're deserializing it, if the data you're reading is from a version before that field was added, use a default value.

    4. Re:Serialization by Anonymous Coward · · Score: 1, Informative
      (Posted anonymously because these are the claims of me, not my company.)

      I have to respectfully disagree with your tone here, although you raise a valid point.

      On my previous project at work, we heavily rely on serialization for managing save states and shuttling data across the network -- not in a game application, but in a database application. We find the serialization pattern to be really powerful in its simplicity, and maybe other /. readers will, too.

      The serialization methods on your objects should not just do a dumb memcpy() of each of its members into a buffer. Yes, if you do this, you're locking yourself into a particular set of members.

      The solution to this problem is so simple that I can't believe you dismissed the entire serialization design pattern because of it. Serialize a one byte version number to the stream at the beginning of each object where a future change is conceivable. Then, if you need to add changes in the future, bump the version numbers of only the structures that have changed.

      Your deserializer code now looks very simple (pseudo C++):

      ver = deserialize<int>(stream);
      if (ver==2)
      {
      . // standard deserialization code
      }
      else if (ver==1)
      {
      . // Code that reads an old stream and builds a new object
      . // in a backwards-compatible way.
      }

      The code in the ver==1 case can do whatever logic is necessary to construct a new-version object with an old-version state. Most of the time, this just means setting some new member in your object to a reasonable default value which preserves the old behavior. Or you might just be changing the stream format for efficiency, in which case the ver==1 case can retain the original deserialize code in its entirety.

      This solution does not give you forward compatibility. A lack of forward compatibility is probably fine for file formats, but generally not okay for network protocols, since you want old and new versions to be able to communicate in both directions.

      Our solution to this is that our network communication objects have slightly more robust serialization/deserialization methods. They start off with a version byte as before (actually a two-byte word of flags, but it's the same idea), but this time preceeded by a four-byte size field. This lets us add fields that older versions will safely ignore.

      Even in version 1, every object calculates how much space it will require to serialize itself, and it writes this number to the stream. Then it serializes as before. The deserialization code uses this number for a simple trick. It reads the size and remembers it, then deserializes the object exactly as any other deserialize method would. After completion of the operation, it compares the number of bytes read to the number of bytes claimed in the stream.

      Now in version 1, these two numbers should always be equal, or else we have a bug. But nevertheless, if these numbers don't match, we read enough extra bytes off the stream to get them to match, ignoring whatever values they might contain.

      So when we decide to update one of our network messages, by adding a field to a network object in version 2, we ask ourselves, "does this network message still make sense to a client who doesn't know about this extra field?" If the answer is no, then we make another network message entirely. Sometimes new version request messages won't be understood by old versions, and we'd rather handle these errors in the network message dispatch code than the serialization code.

      If the answer is yes, then things are beautiful. We bump the version number (or add a flag) to indicate some special version 2 data is on the stream. Then we serialize all the version 1 fields in the same format as before, followed by any version 2 data. So our new deserializer now looks like (pseudo C++):

      size = deserialize<int>(

    5. Re:Serialization by turnerjh · · Score: 1

      I agree with everything you're saying -- my post was aimed at naive serialization as a file saving format, not at more complex forms of serialization.

      Most languages, though, that have serialization routines don't give you the flexibility inherently you're mentioning here; they don't version objects or data and they just plop them in data structures (or, maybe, noticing they aren't format matches and exploding, but definitely not offering much in the way of recoverability).

      The "serialization pattern" isn't much of a pattern really; I mean, it comes down to 'object knows how to save and restore itself' -- whether or not you use operator overloading or subclass Serializable, it's the same basic idea. My point was against implementing the pattern poorly, not the pattern being bad itself; I certainly agree the object should be responsible for persisting and restoring its state (well, in most cases). It's all in the details. Sadly, serialization (esp naive serialization) as quick and easy ways to persist data without really addressing the real-world concerns that tend to pop up.

      The core of my point was: don't rely on other people to solve your complex problems for you, which I think you would agree with :)

    6. Re:Serialization by Elaundar · · Score: 1

      I would recommend C++ for Game Programmers by Noel Llopis. This is a a good book discussing the intersetion between C++, game programming, and OOP and is definately not just another cookbook. The book covers a number of topics such as plugins, abstract interfaces, performance, and memory allocation, but the chapter on seralization covers specifically how to implement seralization for games.

    7. Re:Serialization by Anonymous Coward · · Score: 0

      True, but it's being proposed as an alternative to memory dumps which have the same problem.

    8. Re:Serialization by Anonymous Coward · · Score: 0

      all of it's immediate children

      "its".

  26. Are you serious? by ttsalo · · Score: 4, Insightful

    You save settings/games by essentially dumping data straight from memory to disk? How large projects have you implmented this way? How do you figure out the right parts to write/read? How you ensure that the program memory (data segment, stack and heap) will all be in a consistent state after a load?

    --
    If the road to hell is paved with good intentions, where does the road paved with evil intentions lead to?
    1. Re:Are you serious? by chochos · · Score: 1

      Hey, don't be so harsh. The Microsoft Word development team is a good place for this guy to work in. He obviously doesn't have a clue about OOP and serialization. This is one of those "someone told me I'm doing something wrong, but I immediately said no way. Is there someone here who does stuff the way I do? we're not wrong, right? right?" ask slashdot posts.

    2. Re:Are you serious? by Smallpond · · Score: 1

      He said he keeps all the state in globals so he's not messing around with stack or heap variables. If you know the compiler allocates the globals in order you just need a sentinel at the beginning and end and save everything in between. This is how hibernation mode works on a PC. Its a technique that's been around since FORTRAN common blocks.

      The question wasn't how to do that. That works. Its how to do something that efficient (a single read or write call) using good OOP techniques.

      There probably isn't one.

    3. Re:Are you serious? by Anonymous Coward · · Score: 0

      Perhaps he was part of Microsofts Word team. Memory dumps are a great way to make the file format difficult to reverse engineer!

    4. Re:Are you serious? by kbielefe · · Score: 1
      Its how to do something that efficient (a single read or write call) using good OOP techniques.
      You touched on the heart of the matter. OOP isn't for efficiency of the program, it is for efficiency of the programmer. There is a time and a place for program efficiency, but most of the time programmer (especially maintainer) efficiency is paramount. Saving a game is relatively infrequent and users expect a delay anyway, so saving a few cycles is probably not worth the cost in maintainability.

      I always enjoy the thrill when a well-designed module works perfectly on the first attempt. When done right, each component of an object is trivial and almost tedious to implement, like each individual tile of a large mosaic is unremarkable. However, when you pull back and look at the big picture, the structure of the mundane individual pieces creates a thing of beauty.

      --
      This space intentionally left blank.
    5. Re:Are you serious? by RupW · · Score: 1

      How you ensure that the program memory (data segment, stack and heap) will all be in a consistent state after a load?

      This is actually why Quake 2 saved games didn't work through game updates: it saved entity action state as a function pointer into the game code.

    6. Re:Are you serious? by Anonymous Coward · · Score: 0

      While coding for an un-named *nix like OS, I ran into C++ code that read & wrote memory straight out of the iNode object, scary stuff.

      memcpy(ptr, &inodeObject, sizeof(inodeStruct));

      Thankfully there was an inode struct declared at the beginning of the class. I still have nightmares.

    7. Re:Are you serious? by Anonymous Coward · · Score: 0

      Uh, you are looking way too into this

      Dumping and restoring game data is totally different than dumping the processes state and then loading it and longjmp()'ing it like you describe.

      +4 insightful? If only the mods wrote code...

    8. Re:Are you serious? by Minna+Kirai · · Score: 1

      He said he keeps all the state in globals so he's not messing around with stack or heap variables.

      Right, but if the game state can be described without using heap-allocated variables, it must be a pretty simplistic game. Space Invaders, Pacman, or Tetris is about as elaborate as you'd want to get without using some kind of dynamic memory allocation. So the question about whether or not this guy has ever written a large project is quite appropriate.

      If saving the globals en mass to disk works for you to save games, then there must not be any pointers in your globals, which means your program is avoiding some very common and useful techniques.

  27. Yes, global variables are bad by ZorroXXX · · Score: 2, Insightful
    and you should always avoid using them if you can. There are exceptions when using global variables are ok, but it should not be your normal style of programming.

    Now why are global variables evil? Because they hide information flow. With global variables any function might change them. For instance:

    int x = 1, y = 1;
    if (global_var != 0) {
    x = some_func();
    y = x + (x / global_var);
    }
    How do you know that some_func does not change global_var (possibly to zero)? You do not know that without knowing the complete inner behaiour of some_func.

    If the global variables do not change (only initialised once) then there is no big problems, because then there is no information flow hidden, they are merely some kind of constants. Example:

    const char *argv0;
    int main(int argc, char *argv[])
    {
    argv0 = argv[0];
    ...
    }

    void usage()
    {
    printf("Usage: %s <arg1> <arg2> [arg3]\n", argv0);
    }
    --
    When you are sure of something, you probably are wrong (search for "Unskilled and Unaware of It").
    1. Re:Yes, global variables are bad by ynohoo · · Score: 1

      Sorry, I think you put the cart before the horse. The problem is not global variables, but the limitations imposed on the programmer by the OOPs model. While OOPs offers some benfits over traditional procedural and fuctional methods, it also creates new problems and some nasty hacks to get around them.

      It's back to the old adage "if you only have a hammer, every problem looks like a nail". So expanding your "toolbox" of languages might help you keep a clearer idea of how to solve the problems you face.

      Proudly ignoring the "GOTO considered harmful" zealots since 1988 :-)

    2. Re:Yes, global variables are bad by computational+super · · Score: 1

      Now, let me ask you something. What do you think of everybody else's goto-ridden code? How do you feel about maintaining somebody else's global-variable ridden code? Yeah, that's what I thought. That's how the rest of us feel when we're stuck maintaining your "I'm smarter than everybody else" zealot code.

      --
      Proud neuron in the Slashdot hivemind since 2002.
    3. Re:Yes, global variables are bad by ynohoo · · Score: 1

      It goes with the territory. Sure it can be ugly, but it doesn't have to be, usually just go to exit, or restart loop. Now "alter go to" is a whole other beast!

  28. Do what Infocom did: dump everything to a file by jonadab · · Score: 2, Interesting

    Infocom's approach to the save-game feature was simple and effective: dump
    the entire game -- variables, code, objects, constant data, everything -- from
    memory to a big fat binary file. This is not the most efficient save-game
    mechanism in terms of savegame filesize, and if your game application is quite
    large (as most are today) the save and restore process could take several
    seconds (so, you'll want a progress bar), but it has a couple of advantages:

    1. It's easy to get right, easy to debug, easy to test.

    2. It doesn't matter what kind of objects or data structures your code
    uses; you can use objects, lexical closures, continuations, whatever,
    it won't matter: it all gets thrown in the binary file, so it all
    comes back out when you restore.

    The virtual machine that Infocom developed, called the z-machine, is still
    in use to this day in the hobbyist interactive-fiction community, and they're
    still using this method for saving and restoring games, even though the code
    for the games is written in a different language now (a language called
    Inform which was developed by a hobbyist for this purpose). All the game
    code has to do to effect a save or restore is to issue an opcode, and the
    VM does the rest.

    --
    Cut that out, or I will ship you to Norilsk in a box.
  29. boost.serialization by ville · · Score: 3, Informative

    Boost has boost.serialization which takes care of such things as pointers. Check it out.

    // ville

  30. Objects? by amling · · Score: 1

    I don't code with objects you insensitive clod!

    --
    70e808a22cb027cde4a6abddf6435d55
  31. How can this fail, let me count the ways... by wowbagger · · Score: 5, Interesting
    Summary of your technique:
    Declare various global variables.
    Save game state into them.
    On Save Game, write block of memory out.

    How can this fail, let me count the ways:
    1. Layout of global section can change from link to link due to project changes, thus making saves version dependant.
    2. Non-save game global data can exist between save items, bloating save file.
    3. You can get the size of the save block wrong, and end up not saving the data you need. e.g. you use &foo and &bar as your start and end pointers, but due to a change there are variables after &bar that need to be saved.
    4. If the items you are saving become full-blown objects with compiler generated information (virtual function tables) you are overwriting that data with possibly incorrect data.
    5. No error checking in file - so a recovered file may screw the game up.

    And that's just what I can come up with before my morning coffee.

    Look, I disagree with your instructer about "global variables are NEVER needed" - what, then are stdout/stderr/stdin/cout/cin/cerr, if not global variables?

    However, global variables are like salt - a little may be needed, but too much will raise your blood pressure.

    Again, this is before my morning coffee, but here's a couple of techniques that are better:
    • Define a function to allocate a block of "saved space" - sort of a "save_malloc()" function. Allocate the objects you wish to save via that function. In that function, you grab a block of N bytes at initialization, you initialize an end pointer, and you "malloc" by moving the end pointer (no freeing allowed). You now know exactly what you need to save. You can also write a version # at the beginning of the file, and you can compute a checksum of the data. For OO types, you can fancy this up by writing a "Save_game" base class, and implementing new() for that base class.
    • The more OO approach: Implement a "Save_game" base class. The base class implements a linked-list, with a static member as the head pointer. The class has Register/Deregister functions, and a pure virtual Size() method. Derived classes implement Size() { return sizeof(*this);} and call Register in their ctor. To save game, walk the list. This also allows you to save the size of the object (even better if you use RTTI, you can save the actual type of the object), and to checksum each object.

  32. Persistence discussion on LtU by Rich+Dougherty · · Score: 3, Interesting

    You may find a recent discussion on Lambda the Ultimate relevant to your question.

  33. Antiquated by gregRowe · · Score: 3, Insightful

    The second you hear that word in the context of writing software stop listening to the person who spoke it. Just because a technique is old does not make it inferior.

    --
    There\'s no place like ~
    1. Re:Antiquated by arkanes · · Score: 1
      "Too old to be fashionable, suitable, or useful; outmoded." -- From dictionary.com

      By definition, calling something antiquted is calling it inferior. In the context of software development, it's reasonable to call something antiquted if it's been superceded by a better technology or method - punch card programming is antiquated. Don't turn your brain off on buzzwords. There's a good reason people preach against global variables. It's not just dogmatic.

    2. Re:Antiquated by squiggleslash · · Score: 1
      I suspect the point is though that "Antiquated" is rarely actually used that way. It's usually used as a put-down for older technologies.

      I have a collegue who refuses to do a particular job (pass on authentication from a cookie to an IIS server) the "right" way - which requires programming a small stub in C++ - because C++ is "antiquated".

      True story.

      --
      You are not alone. This is not normal. None of this is normal.
  34. Reverse justification? by erinacht · · Score: 2, Insightful
    You're thinking in terms of your implementation, your method sounds suspiciously like you already had all of your globals and thought, hey I can save these easily, rather than a concious design choice.

    how can I save all of these parameters and their values so that I can later restore the state?
    Restating it as a use case you get,

    Player resumes game at the point they stopped last time.
    This simple change in thought process lets you see that the saved game is no difference in essence to a word processor document, a spreadsheet document or a text file.

    You've identified that a secondary user goal in addition to playing of the game is in the saving and restoring of the state.

    The approach you've taken in using global vars is decent enough, but wrap them up in a class to make things easier to manage. If you think of each variable as a global you're limiting future expansion options - say your game supported multiple players over a network and they each want have their own state stored, your global method would need heavy modification to allow that change.

    Several people have called for a singleton that the rest of your objects talk to, this option, as I understand the term singleton, seems a good way to convert and future proof your existing code.

    Btw - what game is this? Can I download it? Is it GPL?

  35. Singletons or Class variables.. all the same. by mystran · · Score: 4, Interesting

    While I'd argue that global variables are usually a bad idea, I don't see a reason agaist file-scope variables. The rationale goes like this: In OOP you use Singletons when you only need a single variable of a given type. You do it, because while the Singleton gives you global access to the single variable, it still acts as an encapsulation method. This eliminates one of the problems of global variables: unpredicatable modifications, by allowing the Singleton to define what is allowed and what is not. Class variables can do the same, since only the method of the class can touch the variables (provided they are private). But file-scope (static) variables have the same property; the only thing different is that access is restricted not by a class, but by the compilation unit. You still get the same encapsulation. Another question is whether Singletons should be avoided too. Most of the time I like writing code in such a way, that you can run two (separated) instances of the program within the same process by simply create two objects of the main application class. Unfortunately Singletons are often needed to cope with libraries and APIs that assume there's only client within a process. IMHO Singletons (and class variables and methods) are to OOP what IO-monads are to functional programming: they are "hacks" that try to work around the limitations of a given programming model, and trying to minimize the damage caused.

    --
    Software should be free as in speech, but if we also get some free beer, all the better.
    1. Re:Singletons or Class variables.. all the same. by smittyoneeach · · Score: 1
      So you're effectively treating the file as a closure to 'bracket' a few variables.
      Two possible arguments against so doing are:

      legibility might suffer, if the variable declaration is distant from its use

      resource management might be an issue, if you're instantiating "a lot" of stuff before you get around to using it.
      I think singletons have their appropriate uses, and any idiom can be perverted.

      --
      Get thee glass eyes, and, like a scurvy politician, seem to see things thou dost not.--King Lear
    2. Re:Singletons or Class variables.. all the same. by mrami · · Score: 2, Informative
      In C++, at least, using static variables poses a maintainability problem, because even though they may all be file scope for now, as soon as a cross-file dependency arises, you have the initialization-order problem (that is, C++ doesn't specify which file is to be initialized first). Singletons (whether classes or plain-old functions in C++) avoid this problem.

      For those of you who haven't seen this before, check out Effective C++ by Scott Myers (or Meyers; I don't have it in front of me)

    3. Re:Singletons or Class variables.. all the same. by maraist · · Score: 2, Informative

      Depending on your language of choice. Container-based singleton management is an excellent design pattern. In Java, spring and picocontainer manage setter / constructor injection of singletons; possibly even hiding the fact that some objects are session-scope, request-scope or even non-singletons.

      The code is managed by an XML file (or some external configuration); you get the effect of singletons, but the extensibility to swap out which implementation of the interface/base-class you use in which environment. And as you expressed concern, you don't have to worry about running multiple applications with isolated singletons. The component-manager is an instance variable (not a singleton), so you can have multiple isolated environments within the same application.

      Makes testing a LOT easier too. If you use abstract / interface classes everywhere, you can easily swap out mock-instances for testing purposes. (having a testing-environment component-manager configuration file).

      I'm speaking from the Java side, but the basic concept should be applicable to C++ if there aren't already implementations.

      --
      -Michael
    4. Re:Singletons or Class variables.. all the same. by Pseudonym · · Score: 1
      While I'd argue that global variables are usually a bad idea, I don't see a reason agaist file-scope variables.

      Because you can't use a global variable without it first being initialised, and C++ doesn't guarantee an initialisation order for static variables. That's actually the primary argument for singletons: You decide when the singleton object is initialised.

      By the way, the initialisation order problem is even worse for shared libraries, especially in the presence of multiple threads.

      --
      sub f{($f)=@_;print"$f(q{$f});";}f(q{sub f{($f)=@_;print"$f(q{$f});";}f});
    5. Re:Singletons or Class variables.. all the same. by jmccay · · Score: 1

      I wouldn't call the Singleton pattern a hack. It can be combined with a Factory pattern. Usually, you only want/need one instance of a particular factory. That's where a singleton comes into play. A static method for a singleton method with a static variable inside to the factory.
      This insures that you always know when the static variable is created, and you don't need a specific instance of the class to access to get the factory. You can put the static singleton method in the factory class. Singletons have their place in Software Engineering. Many experts far better developers/Software Engineers than most of us see the value of singletons in the proper place. I would recommend the Gang of Four book Design Patterns for more information on Singletons.

      --
      At the next eco-hypocrisy-meeting, count the private jets used to get to the meeting. Should be interesting to see that
    6. Re:Singletons or Class variables.. all the same. by Moses+Lawn · · Score: 1

      All true, but don't forget, class method declarations are distant from use, too (they're in a different file, even!). The "module-specific data" idea (nope, no "globals" here) works in a limited way. If you only have a few, and name them like

      static int gLinesPerPage = 25;

      (and have some setter routine), then you should be okay. It doesn't scale, but can be a reasonable alternative to a more "correct' implementation in the right circumstances.

      The instantiation order can bite you hard, but if you're careful, and don't depend on global vars until your program is at a known state, you should be safe. Relying on a bunch of gXXXConfig objects that refer to each other is a guaranteed disaster, though.

      --

      What if life is just a side effect of some other process and God has no idea we exist?

  36. Your instructor doesn't want checkpointing, either by davecb · · Score: 1
    ... and the unit of migration is the checkpoint (:-))

    --dave

    --
    davecb@spamcop.net
  37. Your instructor is insane by Bastian · · Score: 1

    I don't know what other ways there are to do it - I frequently come across situations where I'm working on something and I am faced with a choice between implementing a feature using procedural techniques with about an hour's worth of effort and some nice easy code, or implementing it using OOP techniques over the course of a day using a ridiculous, bloated, and confusing pile of scaffolding that really isn't needed.

    What's my solution? Easy. Freedom from religion. I think of this IT obsession with thinking some technique/language/library/OS/whatever is objectively superior to all others makes about as much sense as the bridge-building community deciding that xxx style suspension bridge is much stronger than others in long spans, so we should always build suspension bridges, even if all we have to get across is a creek or a small viaduct.

    1. Re:Your instructor is insane by arkanes · · Score: 2, Informative
      I can translate your post as "I don't know OO very well, so using it takes me longer than doing things my way". This is all well and good but not a very helpful way of teaching. It doesn't take any longer to write an object than it does to write a whole slew of global variables, and you don't need to add any special amounts of scaffolding. Incidently, the OO technique often (but not always, although it generally depends on the skill of the programmer) makes it easier to leverage the fancy scaffolding into place after the fact.

      Personally, I have never seen a functional or procedural technique which could not easily be translated into OO. I've seen lots of OO features that can't be translated into functional or procedural, if only because the lack of language support makes using them that way awkward. So in my (admittedly limited) experience, OO is objectively better. I wouldn't make that as a general claim, of course, but I will make a general claim that encapsulating something like global state as an object is superior to using it as a bunch of global variables. Even making a single global struct would be better.

    2. Re:Your instructor is insane by Bastian · · Score: 1

      I can translate your post as "I don't know OO very well, so using it takes me longer than doing things my way".

      (Let's just answer that first random accusation by pointing out that I'm a Squeak user and I make my living programming in Objective-C.)

      Moving on, consider the case of a global state flag. Logic would suggest that since it's a global value, anyway, we might as well just make it a global variable.

      Now some folks would say that this is a big no-no, and that we need to get rid of global variables, and do it the OO way. The best OO way to replace this that I can think of is to create a delegate object and, at the initialisation of the program, hand a pointer to this delegate to every class that might need to know what this state is.

      That's potentially a lot of initialisation. Let's say it's a reasonably complicated program with, say, twenty classes that might need to know about this state. Instead of just checking the state when you need it, you need to add a variable to store a reference to the delegate in all twenty of these classes, and then actually pass that value to those classes, yadda yadda yadda.

      And the question is, what have you gained? You've just spent an hour or so of your time programming all this scaffolding because *oh no, if it's global, everyone will have access to it* in order to make sure that everyone has access to it. Simple problems should have simple solutions.

      And yes, I recognize your point that it's better to cram your globals into some sort of struct or object if you have a whole slew of them. But I would contend that if you are using so many global variables that that becomes an issue, you have some more serious problems with your software's overall design that you should be thinking about.

    3. Re:Your instructor is insane by arkanes · · Score: 1
      If the guy had asked me how to serialize *one* global variable I wouldn't have said anything. But it's never just one, as I'm sure you know. I wouldn't write this in anything like the way you describe - I'd create a GameState object with accessors for all of the variables that would normally be flat global vars. I might spend 5 minutes making it a real singleton. Access to global state would be through the GameState object only (and I might make that state object global) and I'd be done with it. Complicated scaffolding would come later, if I needed it. I see no need for delegates. I think you're misrepresenting the OO proponents here, as well as the "no global vars" dictate. It's not (at least as I use it) an absolute. There's very little difference between a singleton and a global - it's a matter of code cleanliness, style, and semantics. I would use a Singleton here for a) the encapsulation benefits of using an object to store state and b) the ability to control initialization granted by using a singleton rather than a global static object. No delegates. No days worth of scaffolding. I can write this kind of object in my sleep.

      Now, granted, all this presumes a game simple enough that it's reasonable to store all of the game state in a single global object. That certainly may not be the case, or it may not remain the case, and I might need to create a more complicated object hierarchy and serialization tools to deal with that, but the global vars solution is going to fall down a hell of a lot faster.

      As regards to your understanding of OO, I was responding to the content of your post, not trying to make predictions about your knowledge. Regardless, if you actually do spend a day writing scaffolding every time you create an object system you might want to re-think your methodology.

  38. Game Programming != Other Kinds of Programming by John_Booty · · Score: 2, Insightful

    Game programming, and programming in other resource-constrained, performance-critical situations, is quite a different beast than other kinds of programming.

    If you're writing some mundane database software for your office, you want to focus on code maintainability / extensibility which are OOP's alleged strong points.

    If you're coding a game, though, you have an entirely different set of priorities. Code maintainability / extensibility are still great things to have (as you'll surely be developing this code over a long period of time) but they quite often must take a backseat to performance.

    --

    OtakuBooty.com: Smart, funny, sexy nerds.
    1. Re:Game Programming != Other Kinds of Programming by Anonymous Coward · · Score: 0

      Game programming, and programming in other resource-constrained, performance-critical situations, is quite a different beast than other kinds of programming.

      No, it isn't. Your goals should be:

      (1) Reliable code.
      (2) Maintainable code.
      (3) Resource-friendly code.
      (4) Fast code.

      Which, as far as I'm concerned - is no different from any other programming environment.

      When you're working in a team of 10 other programmers, you need the consistency and abstraction that OOP gives you. And I don't know what decade you're living in, but OOP is not slow.

      (1) If you manage to write slow code with OOP, your algorithms are broken.
      (2) If you're allocating and deallocating memory all over the place - you're going to be slow even if you don't use OOP (look up placement new).

  39. Momento... by wed128 · · Score: 1

    Who is JOHN G?

    1. Re:Momento... by 19thNervousBreakdown · · Score: 1

      I don't know, but my car won't start.

      --
      <xml><I><am><so><damn>Web 2.0</damn></so></am></I></xml>
  40. That is not a good counter-example by Tom7 · · Score: 1

    What you're doing is bad because:

    1. Your program cannot be easily extended to support two or more instances of the game without launching two copies of the application. This may not be too important for games, but for other applications with save-state behavior it surely is. With an object storing the

    2. If you simply write out the data, the save binary will not be portable across platforms with different byte orders. You also are limited to the kinds of things you can put there; structured data with pointers will not correctly write out to disk.

    3. All the other things wrong with global variables (generally program hygeine issues).

    It's simple enough to have a "State" object that you fill in with the same information and write a serialization method for it. If you want, you can even make it static, in which case you would be doing almost exactly what you're doing now, but not polluting the global namespace.

  41. Re:eh? mygame.savemethod()?? by etedronai · · Score: 2, Interesting

    Ideally you would solve this problem using an aspect. This is exactly what aspects and AOP were designed for, a way to abstract functionality that does not make sense any where in traditional OO and that cross cuts a large part of the system. Data loggers are also an example of something that should be solved with an aspect.

  42. Trivial by scruffy · · Score: 1

    game.toString();

  43. Re:That is not a good (.. sentence finished) by Tom7 · · Score: 1

    I guess I should finish my sentences.

    "With an object storing the state, you can simply create two of them to have two instances of the games working at the same time."

  44. This isn't an OO question by tod_miller · · Score: 1

    This is merely a design / storage question. It has no special case for OO.

    You must design a save game file format, and this can be as easy/hard in any language.

    Of course, OO like Java has object serialization and XMLSerialization. This is a way of storing data from an object directly into a file in a symmetric way.

    This is not a question about OO, this is a fairly dumb question

    As game saves are file formats, it makes it a design issue, not related to OO mechanics.

    --
    #hostfile 0.0.0.0 primidi.com 0.0.0.0 www.primidi.com 0.0.0.0 radio.weblogs.com
  45. That's not how you implement singleton by Chemisor · · Score: 2, Informative
    No, the proper way to implement the singleton pattern is:
    class Game {
    private:
    Game (void) {}
    public:
    static Game* Instance (void)
    {
    static Game theGame;
    return (&theGame);
    }
    void setup (int, char**);
    int run (void);
    };

    int main (int argc, char** argv)
    {
    Game* theGame = Game::Instance();
    theGame->setup (argc, argv);
    return (theGame->run());
    }
    This lets you avoid new/delete calls, which in your example can create a memory leak if an exception is thrown or some fatal error occurs that crashes your application. With the above implementation Game::~Game is always called allowing you to do necessary cleanup. Good error recovery is especially important in console games, where a crash can leave the terminal in graphics mode and render the machine effectively unusable.
    1. Re:That's not how you implement singleton by xenocide2 · · Score: 1

      Can I ask you how a memeory leak occurs with a system using virtual memory? The application crashes with a fatal error -- as a result the memory is reclaimed by the VM. Do I have some large misconception reguarding the concepts?

      --
      I Browse at +4 Flamebait

      Open Source Sysadmin

    2. Re:That's not how you implement singleton by Chemisor · · Score: 1

      > Can I ask you how a memeory leak occurs with a system using
      > virtual memory? The application crashes with a fatal error
      > -- as a result the memory is reclaimed by the VM.

      Well, yes, you are right. It wouldn't really be a memory leak; I just think of it as such. There would be a memory leak if you tried to use the same code in a non-terminating function and something threw an exception. You'd have to catch it and delete the pointer manually.

      But the real problem still remains: if you let the VM reclaim memory, the destructors are not called. You could have a leak then if you used shared memory or some other global resource. A game would have a good reason to do so to take advantage of X shared memory extension.

  46. Old Dogs and all by WyerByter · · Score: 2, Insightful

    The biggest thing I've seen so far, especially in this thread, is people become used to doing things a certain way and may be unable to quickly adapt to a new paradigm. It is possible to store all the information needed for a game in global variables that can be easily stored, but are available to all and subject to unrelatable effects from bugs and other sources. It is also possible to store all game information in objects, that protect the variables they contain from tampering, accidental or otherwise, but can be difficult to ensure every object is serialized, serialized properly and with a minimun of actual data.

    I personally believe that just as game programming can be different from other kinds of programming, so also can different types of games differ in there best implementations. As such, there may not be a single answer, but each game must be designed individually. The key seems to be to ensure that the structure of the game is consistent and understood by the person responsible for designing the save functionality.

    --

    This signiture copied from somewhere.
  47. Re:eh? mygame.savemethod()?? by eglamkowski · · Score: 1

    That's much how we did it at HDI - a base class from which every object in the game was derived that had virtual save and load functions. Every object that needed saving would implement the save and load functions.

    Although we did from time to time use globals as well, but frequently those ended up in ini files instead of save game files since they were more likely not directly game variables, but rather variables like whether or not to show videos.

    --
    Government IS the problem.
  48. Ignore the books by Tonetheman · · Score: 1

    The truth is that games break all the rules. Global variables are fine in games and necessary in most cases for performance.

    So with that said, write your own routines to dump what you need to disk. Do not just dump memory. It will break when you make a new version of the game that needs to save more information.

    1. Re:Ignore the books by notamac · · Score: 1

      IAAGD (I am a game developer)... do that on my team and I'll do whatever I can to have you off it asap :)

    2. Re:Ignore the books by Sigma+7 · · Score: 1
      Global variables are fine in games and necessary in most cases for performance.
      That statement is simple to counter. Let's say you suddenly realize that you need an additional global variable - you need to make a minimum of three changes: first to add the variable, second to add it to the things to save to a file, and third to ad it to the things needing to be loaded.

      At the very least, it should be encapsulated within a struct (even one at global scope) so that only one change needs to be made. At worst, this would pin the saved games to either one version and/or one operating system - which doesn't even come close to outweighing the benefits for ease of management.
  49. Object Oriented and Persisting by stumbler · · Score: 1

    Object Oriented is a way to abstract code, and it does not impose or preclude a specific technique for saving configuration or state.

    In truth, how you define your objects will play a big part and what you are persisting, and thus, how to go about doing it.

    I encourage you not to discount your instructor, but rather, consider your object model, game, and decide what actually needs persisted.

    (Compare, for example, persisting a Chess Game vs. A Space Invaders Game, vs. a game like Morrowind.)

    Like any paradigm, you can find a really horrid way to persist via O.O., or a really elegant ways to deal with this problem too. It's not the paradigm, but the programmer that determines the elegance of the code.

    If you are writing Java, Peter Coad in his book Java Design (ISBN 0-13-911181-6) gives a wonderful way of leveraging objects to make clean and elegant object oriented code, (though it does not deal specifically with persistence.)

    Good Luck

  50. Use these passcodes: by Chemisor · · Score: 2, Funny

    Use these passcodes. Then we won't have to memorize another 120 meaningless numbers.

  51. Pragmatic solution.. by Anonymous Coward · · Score: 0
    This method uses few global 'services' which maybe implemented with global variables but it works quite well.

    Each class which must be saved inherits class which constructor/destructor registers object to a (global) list of to-be-saved-objects. In addition this class has an abstract read/write to/from file method which writes objects state to a file.

    save() method iterates through list of saved objects and calls obj->write(). Load method tries to load() object with static ClassName.read() methods till it read() is succesful.

    Pointer's are hard to handle so each class must also have a unique id which must be saved instead of pointers write(pointer->getID()) and pointer = IDService->getPointer(ID). Loading must be done in two steps:
    1) load all objects, save IDs to pointer variables.
    2) replace all IDs with real pointers.


    --
    Tomas Ukkonen

  52. Id does it quick and dirty. by Anonymous Coward · · Score: 2, Informative
    The major problem with persisting an object graph, is maintaining the internal relationships without causing duplication on load. To that end, Quake2 (and possibly its successors) has a fairly radical approach to this problem that works really well.

    Basically, keep all your game state in a large array of entities:
    #define MAX_ENTS 600 // or some other large number
    typedef struct Entity{
    int ent_type;
    int health;
    int armor;
    int behavior_state;
    Entity* target;
    Entity* leader;
    // and so on...
    };
    Entity[MAX_ENTS] game_ents;
    (BTW, the scheme above has certain distinct advantages over freely new/delete managed objects, via object pooling)

    When it comes to persist, simply rewrite all the internal pointers to other entities as indicies and dump the entire list of entities to disk. Loading from a save file is merely the same operation in reverse: translate offsets into pointers.
    FILE* save_file = fopen("save.dat","wb+");

    // save object graph of Entities via offset translation
    for(int i=0; i<MAX_ENTS; i++){
    Entity temp = game_ents[i]; // copy
    temp.target = (Entity*)((int)temp.target - (int)game_ents);
    temp.leader= (Entity*)((int)temp.leader - (int)game_ents);
    // you get the idea...
    fwrite(&temp,sizeof(Entity),1,save_file);
    }
    fclo se(save_file);
    (or merely stuff the base array pointer into the save file and just retranslate on load)

    The tradeoff is a quick save/load feature for less flexibility in your game entity scheme (relatively inflexible for oop, but can be done). Also, you'd need an array for every base type you wish to mantain in a proper graph.

  53. from one OOP "fanatic" by Anonymous Coward · · Score: 0

    What do you die-hard object-oriented fanatics have to say about this, and what method they would you use for saving games?

    I'd say I'm being trolled, and won't do your homework for you either.

  54. Not "under any circumstances" eh? by snorklewacker · · Score: 3, Interesting

    Here's the deal: when your professor tells you that you're never ever ever ever ... ever ever ever ... ever ever (etc) supposed to do something, then as the poster who quoted Miss Manners mentioned, you've got a pretty good idea what the answer to the exam question is. Any professor who's been in the real world (usually people who retire into teaching) will tell you that there's exceptions to any rule.

    Games, with their relentless demands for resource efficiency, will have you breaking lots of rules. Game saves are one of the first walls a junior game designer hits. They've written this fabulously interesting game, unpolished of course, but it's got real potential. But the saved games are two megs each, and take 15 seconds to write out. There goes your console version. You now have to start cutting all kinds of corners to get those save times and sizes down, and that may mean a sacrifice of architectural purity.

    To wrap it up, you probably do not want to blindly serialize all your stateful objects into persistent storage and leave it at that. You can and probably should do that while developing the game (be sure to version your objects while you're at it), but when you need to get efficient, you need to start relentlessly trimming the "serialized" form, and seeing what you can build up, recreate, or even just leave out (e.g. a save game in a RTS probably doesn't need all the scorch marks saved). Then instead of serializing to a stream to persistent storage, you want them to simply notify a "state container" with a reference to themselves (the container can egregiously violate encapsulation -- use inner class adaptors or private inheritance if you're paranoid) and that container can index into a memory segment. Then you just write that segment out to disk. Version the damn thing, so if you patch the game, you're not completely hosed. Keep in mind that you're getting RAM 4K at a time, and writing it to disk in bigger chunks, so don't be too stingy.

    Now go do the rest of your homework yourself.

    --
    I am no longer wasting my time with slashdot
    1. Re:Not "under any circumstances" eh? by bluesourcecode · · Score: 1

      I was just going to say that. Combining the above advice with some of the other logical post, the complete process would be to model the important informations (enemy position and health, ammo, map level, xyz positon, etc) and serialize those. Some object maybe too large to serialize, so make a SaveGameObject, which will go around collecting information from other objects. (Weird things can happen in these cases like enemies forgeting they saw you when they are reinstanciated)

    2. Re:Not "under any circumstances" eh? by Moraelin · · Score: 1

      Very good advice, but if I'm allowed two minor comments:

      1. Personally I wouldn't say that trimming the serialized form is breaking OO design. The way I see it, even OO itself is built upon the concepts of "class" and "instance". The instance doesn't have a complete copy of the class's code, nor the constants, but just what's different: the actual variables. For the rest, there's the pointer to the actual class code.

      Dunno, I found it just natural to apply the same thinking to serializing objects, game or no game. E.g., if in the game you have an upgradeable H&K MP5 SMG, I'd think of it as "a weapon object with a mile long list of stuff like range, burst size, etc" but rather as "an instance of the H&K MP5 SMG 'class'".

      Well, not a "class" in the sense of a code block, but a game object definition "class". There is a master definition of a H&K MP5, and the actual objects in the game are instances thereof. All that needs to be saved is what can be _different_ from the master object: ammo remaining in clip, upgrades (e.g., a silencer), status (e.g., if it's jammed) and such. For the rest, there's a pointer (or id) to the master H&K MP5 "class".

      E.g., for the RTS example, you can treat the map as a "class", and only need to save what can be different from that definition. E.g., buildings.

      Seems to me like it cuts down the saved size a lot, without breaking OO design at all. In fact, it's IMHO just a natural extension of it.

      2. In all that comment, you haven't actually proven his professor wrong. In fact, if anything, your approach just says that his professor _is_ right and he is wrong.

      Blindly saving a block of global variables, which is what he wanted to do, just prevents all those optimizations. _And_ almost prevents versioning.

      What I'm trying to say is that, ok, his professor is technically wrong about the "never ever" part, but it's still not a bad thing to teach those students. Even if one ends up cutting corners, it's better to start with a good idealized design and then cut corners, than to start with an unstructured turd and try to polish it.

      It's easier to implement those good optimizations you've written there, when one starts from a good OO design, than when one starts with a block of global variables written en masse to disk.

      E.g., the trimming of data is actually quite easy to do when each kind of object is a class that knows what it can trim. (E.g., a weapon class can know what upgrades a weapon can have, compared to the master definition, while a map class can know that scorch marks aren't needed.) Whereas if one started with a huge block of global variables, implementing those optimizations will mean rewriting most of it from scratch.

      --
      A polar bear is a cartesian bear after a coordinate transform.
  55. Crime and Punishment by ratboy666 · · Score: 2, Insightful

    Your compiler is not obligated to keep the variable ordering. This means that some parts may not be saved.

    Also, the data file can be "hacked", and your program can be convinced to take other paths (think security -- this includes arbitary code execution). Defending against this means checks on every data item anyway.

    If you have C++ global objects, the function pointers in the objects can be overwritten (accidentally, by changing revisions of software, or maliciously).

    Global variables are bad, because they introduce the POSSIBILITY of coupling. Generally, if you can do without them, its better. Because once the possibility of coupling is introduced, it is very difficult to prove that it /hasn't/ happened. It may not affect you (the Programmer) but will affect the Maintainer. She will spend considerable time wondering if a change is safe.

    Things with global variables tend (I said *tend*) to be non-reentrant. Which makes reuse a pain. It also makes a conversion to threading painful.

    Global variables can (accidentally or purposefully) provide communication channels /between/ parts of the program that are not documented, or are very difficult to document.

    I tell my students: Rule 1: Global Variables are evil; Rule 2: See Rule 1.

    As a Student, it is your responsibility to absorb as much Zen of Programming as you can. Believe your Teacher in this instance.

    Ratboy.

    --
    Just another "Cubible(sic) Joe" 2 17 3061
  56. Re:Do what Infocom did: dump everything to a file by metamatic · · Score: 1
    Infocom's approach to the save-game feature was simple and effective: dump the entire game -- variables, code, objects, constant data, everything -- from memory to a big fat binary file.

    And have 4GB game saves for your multimedia DVD-ROM game? That'll get you some interesting reviews.

    Plus, that's not actually what Infocom did, for the similar reason that 128K game saves wouldn't have been acceptable back then.

    --
    GCHQ Quantum Insert installed. If only our tongues were made of glass, how much more careful we would be when we speak
  57. Yeah, just like not using "goto" by Cthefuture · · Score: 1, Insightful

    Pffft, whatever.

    There are times when a goto is best. There are times when a global actually is the best and correct answer. There are times when a more object oriented answer is best. There are times when a functional programming solution is best.

    We should learn from our experiences but you don't throw the baby out with the bath. Everything has a place. I hate it when (especially teachers) say things like "never use globals." There is a time and place for everything. What they really need to do is show a specific example of when and why you should not use globals.

    --
    The ratio of people to cake is too big
  58. on game globals by i0wnzj005uck4 · · Score: 1

    I'm not quite sure what you mean by globals exactly, but I'd assume you're speaking about game state changes (big things, like You Got the Red Key or You Finished the Ninja King and Therefore Now Have the Sword of Green Fire).

    I don't know how proper this is, but I accomplish this in a variety of ways depending on the complexity of what I have in mind.

    Inside my main game object I always have a gamestate object, which is something like this:

    class cGameState
    {
    long int statelist[MAX_STATELIST_SIZE];
    public:
    cGameState(); // memset the whole thing to 0

    int getState(int which); // returns value from statelist
    void setState(int which); // sets a value in the statelist
    };

    Depending on whether or not speed is an issue I assign enumerated values as state names or I use strings loaded from disk at runtime. When I need a value then, I can talk to the gamestate object. When I need to write it out, I add a gamestate::save(FILE *fp) function. (No, I don't use iostreams. Never liked them.)

    When it comes time to save a game I usually give every object that would need to be saved its own saving function, something like: entity->save(FILE *fp). A few people would say that this is bad, but in practice I find it quite a nice way of going about things regardless of object bloat. I then have the main game object step through its lists of objects, telling each to write in turn, bracketed by counts of objects or section headers or tags or the like. So for example, the game state would write, in order:

    - Magic indicator number (in my games usually 0xD00D)
    - save file version, program version
    - save file player name
    - gamestate array (sizeof(whatever I chose for the array) * MAX_STATELIST_SIZE)

    If I need to save entities:
    - entity count
    - entities as so assuming an open FILE *fp:

    for (i = 0; i numTotalEnts; i++, entities[i].save(fp));

    I haven't coded in a while, so the above is rightfully suspect.

    --
    - Cloud
    1. Re:on game globals by Blakey+Rat · · Score: 1

      That's basically serialization, and yes it's a very good technique to use in C++.

  59. Make Your Own Namespace by basking2 · · Score: 1

    When ever I write anything of any substantial size I typically go through a stage of coding libraries. One of the libraries I have found *invaluable* is the "name space." All it is is an object that takes a string and a primitive datatype (int8, int16, int32, int64, string, or byte array) and an name space. This lets you make a tree of values which you call write() on the root value and the whole tree is written to a stream. This lets you push configs to a file or over a TCP stream and read said objects back in SIMPLY! Typically I put a "modules" namespace in the root namespace in which I store namespaces named after all the modules I load. SO, when I load my config file I do... value = myNameSpace.find("module configs"); value = value.find(moduleName); Then I pass that namespace to the module and let it configure itself. This makes global values either totally obsolete or limited to a single global value (depending on how you prefer to organize your project). This also makes configuraton files VERY easy to manipulate. I can't say enough good things about this pattern of value management! MS has used it by introducing the registry which has its problems, but is a very similar idea of hierarchical data storage indexed by names. Hope you find this interesting. :)

    --
    Sam
  60. not a "student" question by jeif1k · · Score: 1

    This is the largest load of crap I've heard in a long time, especially directed to someone who's a student.

    The guy took a C++ class; he is now writing games (presumably for fun or for a living). So, this is not a "student" question, it's a "how do I get this done in the real world" kind of question.

    as opposed to being a reactionary turd who'll refuse to better himself.

    Well, at least he isn't someone who automatically follows every new fad. Global variables are still useful and reasonable, as is saving them by saving a chunk of memory. It's a simple, effective, time-tested technique. It has some (known) limitations, but if he can live with those, it may be the right technique for him.

    But he wants to know if there's a better way.

    And the answer is: there isn't a uniformly "better" way. There are different ways of solving this problem that involve different tradeoffs.

    1. Re:not a "student" question by arkanes · · Score: 1

      If he's satisfied with what he's doing, then he wouldn't be asking questions. Moreover, anyone who is doing anything of any complexity who is satisfied with using global variables and serializing via memory dumps isn't someone who's doing anything I would want to be associated with. It's bad code. I suppose as long as he keeps it to himself without asking anyone else to run it there's no harm.

    2. Re:not a "student" question by computational+super · · Score: 1
      follows every new fad

      Tell me about it. This "OO" buzzword is never going to catch on - it's been over 10 years since it became mainstream, but I'm sure we'll all come to our senses and go back to the purity of fortran any day now. Next thing you know, he'll be writing GUI interfaces instead of command-line applications, too.

      --
      Proud neuron in the Slashdot hivemind since 2002.
  61. XML-like Tree serialize... by Leadhyena · · Score: 2, Informative
    Whenever I had to save a game state I would write a tree-like structure, and pass a file object around. Choose the format of your save file to be XML-like in the sense that you have some sort of marker that identifies the beginning of a game object and the end of a game object, and that your objects are nestable.

    Next, create a utility class that contains functions that can read a file up to a marker and return what was read, and another that can identify a marker and jump to the appropriate class. When you do the save, tell the game to save by storing simple objects in order and then passing the save call to the complex objects in order. Those objects will do the same thing, delimiting their objects by bracketing them. Then when restoring, read to each tag and jump to the appropriate class constructor. In effect, you're crawling the tree of objects that are being used for the game.

    The advantage of this method is that you store exactly what you need to store, adding game objects is a breeze and almost append themselves to the game tree, and if you expand your game you can tell the reader to ignore tabs it can't identify and work around them, meaning that your older save-files won't break the newer version.

    One other thing to keep in mind is that make sure that no constructor actions take place until everything is loaded, so that you don't have say player objects trying to render on a board that hasn't been constructed yet. Make sure you have both a constructor and an initializer funciton for all of your game objects.

  62. Memento by cratermoon · · Score: 1

    What you really want is the Memento Pattern. Intent: Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. I agree generally with the other posters who warn against binary serialization and memory dumps. Define a format for writing out the memento object(s) that captures the necessary state but is easily parseable and extendable. A couple of formats that you've probably seen are the windows-style INI files and some kind of XML. There are lots of others.

  63. Save Game method abstraction by jgardn · · Score: 1

    No, what I would do is write a save_game and restore_game method in a universal subclass for all game objects. It will know how to serialized all the objects contained within a game object, and how to read them back out. That way, I only implement the save/restore method once.

    --
    The radical sect of Islam would either see you dead or "reverted" to Islam.
    1. Re:Save Game method abstraction by torpor · · Score: 1

      yeah, thats what i would do too .. only have to write it once, let it propagate throughout the whole hierarchy as needed ..

      --
      ; -- the corruption of government starts with its secrets. a truly free people keep no secrets. --
  64. state stacks by ironfroggy · · Score: 1

    I accomplish most of this through the use of State Stacks. A State interface is defined and for each state of the game ("in play", "in menu", "paused") I create a class. States can be pushed onto the stack, popped off the stack, or replace other states on the stack (just a few pops and a push, of course). This works well because when you want to Pause, a PauseState class instance is pushed onto the top of the stack. Events are handled by state objects and the one on the top gets first dibs. It then deligates if the events are to be passed on to states below it or not (a "typing chat message" state, for example, might pass mouse events on to the "in play" state below it).

    What does this have to do with save games and globals? Well, saving a game is merely a matter of serializing the "in play" state. As the state object holds all the data pertaining to it, there is no need for nasty globals.

    The point of all this is simple: you really don't need to have global data. There is always a way to represent it in an object-oriented fashion. And, of course there are advantages. My layout can easily support interesting things, such as mini-games and the like, without any fuss at all. I can even use the exact same code to write a stand-alone puzzle game, or a mini-game. I'm just saying, it isn't all talk when people say everything should be componentized and globals are bad. There are serious benefits, beyond just academic theory.

    1. Re:state stacks by Anonymous Coward · · Score: 0

      I think your "state stacks" sound suspicously like "windows". Don't get me wrong: I'm not bashing it. In fact, I think this is a great metaphor for programming, and it is an example of natural OOP that can be implemented a procedural or functional language without any special syntax. Thank you for helping me see another to look at this familiar style of programming! I feel silly for not realizing this connection before. *pat self*

  65. no need for global variables here by jilles · · Score: 1

    Encapsulate your data with a proper API (getters and setters and maybe some additional stuff). Maybe throw some factory method in that returns the appropriate 'game' object given e.g. a filename.

    This way you can adjust which instance is used in the game or change the implementation of the API without touching the game code or changing the way the object is persisted.

    Your application code just calls
    GameProps foo = GameProps.getInstance("somefilenameorwhatever");
    foo.getProperty1(..);
    foo.setProperty2(..); ...

    getInstance does stuff like opening files on disk if needed and instantiating the GameProps class. It may keep a private reference to a singleton instance or maybe cache some frequently used instances.

    This is a nice, lightweight OO solution. You gain a lot of control over the behaviour without any real price and without breaking encapsulation, unlike your global variable.

    --

    Jilles
  66. Yeah.. by newr00tic · · Score: 1

    ..then I guess a way to ensure better replication up to that point (across versions), would be to code version-info into the saved files, and also, to take use of 'diff' files, for each patch released. So, if you would load an older save on a new-patched game, you could perhaps 'calibrate' using diff-files, so as to upgrade the deprecated moves, to suit the new patches..

    I've no idea why I'm writing this; just seems interesting..

    --
    A horse can't be sick, you know, even if he wants to.
  67. Save gamer by pt99par · · Score: 1

    I would have made an asbstract factory for saving games and an interface for the game saver. IGameSaver* saver = gameSaverFactory.Create(); saver->Save( gamedata ); Kind of like that.. so that the saving of game data is separated from the data model. This gives me the abillity to save to disk or network or any place i want by just making a new class that implements the IGamesaver interface.

  68. I use XDR myself by noelbk · · Score: 1

    I needed to serialize C structs too (for a network protocol), and writing serialization code for every message was too tedious and error prone. So, I made my own serialization package based on Sun's XDR code.

    This is from the README:

    The bkxdr library is for serializing C data structures to portable
    compact byte streams or XML. You describe the data structures in a
    C-like type library, and this library generates the C header file and
    serialiation code for you.

    It handles almost all C types including strings, arrays, pointers
    (liked lists and graphs), and binary blobs. It's handy for sending
    structs as network messages, saving complex structs to disk, or making
    deep copies of linked structs.

    The code is based on Sun's XDR specification which is used to
    serialize structs for RPC. XDR is a standard (rfc1832) compact
    machine-portable binary format. I've modified the library to use XML
    instead of the binary format, if you choose. So, you can serialize
    your structs to compact portable binary or readable XML.
    You're welcome to it if you like, just send me a reply.
    1. Re:I use XDR myself by Anonymous Coward · · Score: 0

      Sounds like it'd be worth a look. Drop me a line/link at jskarzin@shaw.ca, if you'd be so kind.

    2. Re:I use XDR myself by noelbk · · Score: 1

      OK, It's at http://www.bkbox.com/~noel/bkxdr-0.01.tgz. Let me know how it goes.

  69. OO isn't for games by AmiMoJo · · Score: 1

    OO isn't really for games, or any engineering problems for that matter. It's designed to model business or "real world" concepts.

    Thus, if you try and use it for engineering style problems as you want to, you will always run into, shall we say, philosophical problems. Many coders just use OO as a way of allocating memory and grouping related functions anyway.

    --
    const int one = 65536; (Silvermoon, Texture.cs)
    SJW, n: "Someone I don't like, and by the way I'm a fuckwit" - AC
  70. Huh? by Hard_Code · · Score: 1

    So... make a single object called GameState, and that object can contain all the fields you want:

    struct GameState {
    all
    your
    shit
    nicely
    contained
    here
    }

    it beats a gazillion global declarations and 'extern blah' everywhere.

    If you don't like struct, at least throw a namespace on it:

    namespace GameState {
    your
    fields
    here
    }

    /* better yet, use a language where there isn't a discrepency between interface declaration and implementation, and source unit and class... */

    --

    It's 10 PM. Do you know if you're un-American?
    1. Re:Huh? by Hard_Code · · Score: 1

      As others have mentioned you can of course use a singleton object, which buys you not having to declare a global object at all because you can access the object through a static accessor (getInstance()), however, as I found recently, there often isn't a guarantee as to when static object data is initialized (i.e. you could call getInstance() before the struct was actually initialized, assuming you used a struct and it was statically initialized). Your call.

      --

      It's 10 PM. Do you know if you're un-American?
  71. Re:Do what Infocom did: dump everything to a file by vslashg · · Score: 1
    Plus, that's not actually what Infocom did, for the similar reason that 128K game saves wouldn't have been acceptable back then.
    Actually, it is almost exactly what Infocom did. The only place the grandparent is stretching the truth was that only the volatile memory of the virutal machine was written to disk. Everything after a certain address in the Infocom VM is ROM. Of course this part of the VM data didn't have to be saved in the state files. Other than that, the original save files were indeed a dump of the RAM portion of the VM's memory.

    The area of RAM in the Infocom VM is actually rather small, which is why this worked.
  72. Also... by SteeldrivingJon · · Score: 1


    Nobody wants their game state written all over their body.

    (Well, some hardcore gamers might actually get off on that.)

    --
    September 2011: Looking for Cocoa/iOS work in Boston area Cocoa Programmer Quincy, MA
  73. How about simply using an object database? by Carl+Rosenberger · · Score: 1
    An object database will do all that you want for you:
    database.store(object);
    Behind the scenes it should:
    - analyse your class structure, even if it changes
    - ensure everything you do is transactional
    - provide fast querying functionality for you with indexes
    - help you improve your memory management by allowing to unload unused objects from RAM

    Here are some object database links:
    http://www.cetus-links.org/oo_db_systems_1.html
    http://cbbrowne.com/info/oodbms.html
    http://www.db4o.com/
    1. Re:How about simply using an object database? by Tablizer · · Score: 1

      Or a relational database and chuck OOP. (Just a suggestion guys, ignore it if you don't like it.) Databases in general are probably better for slower-paced games because garbage collection and other database-related operations may create occasional burps in execution speed. If you are going to use a database in a high-speed game, then perhaps have a way to keep movement going while the database action is happening. This is similar to a human action in which you just keep running while thinking up a plan. You wouldn't stop running away from a bear just because you need to do some strategic thinking. Thus, you have a sychronous "dumb action" mode that can keep pace, and an asynchronous connection to the database or "smarts" engine that may take a bit longer to think things out. The in-progress characters will use the "smart" engine results when it comes available and start acting on it/them.

  74. First define global variable by Anonymous Coward · · Score: 0
    What is a global variable? It's anything that's accessible from anywhere in your program, right? That includes save files. "Huh?," you ask. That's right! Consider:
    /* in global.h */
    int readglobal (const char*);
    int writeglobal (const char*, int);

    /* in myfile.c */
    void dosomething (void)
    {
    writeglobal ( "x", 42 );
    }
    "Wait, Wait," you protest. "But that's not a global variable! The value seen by the program can only change when you call the readglobal() function." True, but let me show you another example:
    /* in global.h */
    int* getglobalpointer (const char*);

    /* in myfile.c */
    void dosomething (void)
    {
    int* px = getglobalpointer (x);
    *px = 42;
    }
    "Ohhh. I get it!", you exclaim. "You're making an analogy to fread, fwrite and mmap. And you're saying that something can be a global variable even if it does not have an entry in the global symbol table." Correct, grasshopper. Global variables are what make programmable software possible. I will leave you with one final thought:
    What do you call a Turing Machine with no write head?
  75. He's right. by AnotherBlackHat · · Score: 1
    "I took a course in C++ a year ago in which the instructor claimed that global (file-scope or inter-file-scope) variables were antiquated and not to be used under any circumstances.


    IMO, he should never use the global variable main().

    "global" is nothing more or less than a particular scope.
    "Variables" can be loop-wide, procedure-wide, file-wide, program-wide, system-wide, or even internet-wide.
    There are uses for each scope.

    It's probably true that you shouldn't use a wider scope if a smaller one will work, but I don't buy any arguement that one should never use a particular scope.

    -- Should you believe authority without question?
    1. Re:He's right. by nate+nice · · Score: 1

      Those are all good points. I've always thought that a variable should have as minimal scope as possible. If you're using a lot of global variables though, most likely your program suffers from poor design. As for saving data in a game, I would think that if you're using the OO paradigm, every object should be able to save itself, or at least have a delegated class that does this.

      There was a time when global variables were worse in that having a large stack of them would eat up a larger percentage of the users limited memory, but with todays systems the only problem I see is design and possibly identification issues if you're not the least bit careful.

      --
      "If you are a dreamer, a wisher, a liar, A hope-er, a pray-er, a magic bean buyer ..."
  76. Do it like the pros by Klaus+Wuestefeld · · Score: 1

    When all the game data fits in RAM, you can do it like Age Of Empires or Starcraft do it: http://www.prevayler.org/wiki.jsp?topic=MajorCompa niesUsingPrevalence When you can't fit all game data in RAM, you better resort to an OODBMS such as http://www.db4o.com/ See you, Klaus.

  77. Exactly! by janwedekind · · Score: 1

    If you work with C++, boost-serialisiation is the best solution, as it also is part of an upcoming standard!

  78. Re:Do what Infocom did: dump everything to a file by jonadab · · Score: 1

    > Plus, that's not actually what Infocom did, for the similar reason that
    > 128K game saves wouldn't have been acceptable back then.

    Okay, I admit, I oversimplified, because I didn't want to discuss the finer
    points of the z-machine memory model. Technically, they only dumped the
    bottom portion of memory, the read/write portion, or in some cases the vm
    (essentially) binarily diffed the in-memory game against the copy on tape
    or disk or cartridge or whatever. But these are implementation details;
    the fundamental essense of the approach is what I said in the first place.

    --
    Cut that out, or I will ship you to Norilsk in a box.
  79. Simply untrue. by Anonymous Coward · · Score: 0

    I've been in the industry 8 years (including 3 published PS2 and Xbox console titles) - and we've used OOP on every single project.

    When projects take more than a few months (such as an average 18 month development cycle), reliablity and maintainablity are FAR more important than raw speed. If you nail those, speed tends to automatically follow as your code is clean. Besides, the abstraction that OOP gives you by using classes for your game object heirachy is quite fantastic and you'd almost be silly not to do it.

    Yes, speed is important. But OOP alone does not automatically give you a speed handicap. C++ features like templates and heavy use of the STL might give you problems - otherwise, no problem. If your algorithms and code execution path is bad, your game is going to have performance problems even if it doesn't use OOP.

    In short: OOP is ideal for games.

  80. No global vars? by LWATCDR · · Score: 1

    Yea that is what they teach in school but is rarely used in the real world. Now the idea of just writing out a block of memory to save anything is very old school.
    Why should you avoid it? The biggest reason is what if the file gets corrupt? Odds are your program will crash and burn if you try to load the file. It is also hard to recover the file if it damaged. Not that big of an issue for a game but a killer for other types of apps.
    Yes the way to do a save is for each object to have a save method. The format should have many levels of error checking and I would avoid binary storage.

    --
    See my blog http://ilovecookes.blogspot.com/ for light hearted technical information.
  81. Sorry I Haven't Gotten It Yet by GreyArtist · · Score: 2, Informative

    First, I'd like to thank everyone for all the well-written responses I've received to my question, but...

    I think that in some instances the crux of my problem may have been overlooked. The title of this post may have been somewhat misleading (my fault), but I was really more concerned with the avoiding code bloat aspect of my question than the "how to save a game state" part, all while maintaining object-oriented methodology. Some people seem to consider this a silly question, as the object-oriented methodology supposedly allows any technique you want. I beg to differ. When I started out programming many years ago (writing BASIC on an Apple II), I instinctively wrote top-down, procedural, and somewhat structured code that had very few GOTO statements (maybe some when I had mismanaged line numbers). The methodologies I just mentioned do allow for any technique you choose to employ, and can rival the efficiency of any of the spaghetti code that came before. I have never found the same thing to be true for object-oriented methodologies.

    Some of my concerns about the techniques mentioned:

    The singleton technique seems to be a politically correct label for a global data structure. I don't think my ex-instructor would approve (but seriously, it does seem to be somewhat of an anti-oop construct).

    The object serialization technique is basically a sophisticated term for the MyObject.savemyself() that I mentioned at the top of the post. Of course the game data would need to be serialized, rather than letting objects write randomly to the save game file with a brief header describing who had done the writing. My overriding concern with this technique is that it would involve so many function calls for a game that included say, 40,000 objects. My secondary concern would be that depending on the class hierarchy, many different versions of a save game state function would have to be employed. While one post claimed that a universal base class would be able to write the game state out for every derivation, I cannot believe it. Derived classes will always have extra features that affect the game state which they will need to handle themselves (their parents and children can't do it for them).

    After spending 15 years trying to clearly understand the object-oriented philosophy (even taking a class at my age), it occurs to me that the object-oriented hype seems to be an attempt at scaling down the operating system philosophy to fit single applications. Objects are "mini-programs" that are supposedly decoupled from the surrounding super-program and protected from dangerous and misbehaving code in other modules (of the same super-program). Funny that after 20 years of procedural programming, I have seldom beheld any of the claimed symptoms of not using object-orientation.

    I think the industry went in totally the wrong direction. We should have been fixing our operating systems to more closely mimic the single application philosophy...

    But I would still be interested in hearing about a truly efficient, simple, elegant, and nearly code-less object-oriented solution to the save game state problem.

    email: aofi7@hotpop.com

    1. Re:Sorry I Haven't Gotten It Yet by noelbk · · Score: 1

      Yes, you shouldn't have to write serialization code by hand. And there's no C++ library I know that serializes objects (perl and python have such facilities built in.)

      If you want a code-less way to save lots of state, and aren't worried about cross-platform portability, try mmalloc: http://www.math.utah.edu/docs/info/mmalloc_1.html. That can allocate all your game state variables in memory which also persists in a file on disk, automatically.

      To create game state:

      fd = fopen("my_state_file", "r+");
      md = mmalloc_attach(fd, 0)
      my_state_root = mmalloc(md, sizeof(*my_state_root));
      mmalloc_setkey(md, 1, my_state_root);
      // add linked list of state structs
      mmalloc_detach(md);
      fclose(fd);

      To restore game state instantly (no serialization code at all):

      fd = fopen("my_state_file", "r+");
      md = mmalloc_attach(fd, 0)
      my_state_root = mmalloc_getkey(md, 1);
      // change state, free and alloc nodes
      mmalloc_detach(md);
      fclose(fd);

      Note that when you restore, all data (including pointers) will be valid without any serialization code at all. But, the save file will not be portable between different architectures.

      HTH

      --Noel

  82. "There goes your console version" by tepples · · Score: 1

    But the saved games are two megs each, and take 15 seconds to write out. There goes your console version.

    Sure, some developers toss out the possibility of a console port when they discover that they can't get the savegame size anywhere near 128 KB (16 blocks on GCN memory cards), but for others, it's "there goes your console version" long before the first line of code is even written, even on games whose save state would take only 1 KB, because the console makers, who control the bootloader, won't even talk to startup game development firms.

  83. Implementation by fprog26 · · Score: 1

    #include "stdio.h"
    #include "stdlib.h"

    typedef struct gamedata_s
    { // my global variables

    } gamedata_t;

    class GameData
    {
    public: // get/set functions to access "global vars"

    GameData()
    {
    pFile = fopen(fname, "r+b");
    memset( &data, 0, sizeof(data) );
    }

    virtual ~GameData()
    {
    if (pFile)
    fclose(pFile);
    }

    int seek( size_t game_no )
    {
    long offset = game_no * sizeof(data);
    return fseek(pFile, offset, SEEK_SET);
    }

    int load()
    {
    return fread(&data, 1, sizeof(data), pFile);
    }

    size_t save()
    {
    return fwrite(&data, 1, sizeof(data), pFile);
    }

    private:
    static const char* fname = "gamedata.bin";
    FILE* pFile;
    gamedata_t data;
    };

  84. It also prevents anything from being dynamic by Moraelin · · Score: 1

    All your 5 points are very insightful, but if I may be allowed to add one from experience:

    6. Blindly writing a block of memory prevents using _any_ kind of pointer, not just virtual function table pointers. _Any_ kind of a linked list, hash table, dynamically sized array, etc, suddenly becomes taboo.

    For example, how's a fixed memory block going to accomodate the inventory in an RPG? Have a fixed buffer for every single object in the game, for example, saying how much of each you've got?

    Sounds good until you realize that this prevents any kind of mod, and you can't even make an official expansion pack that doesn't require starting the game from scratch. You can't add a new object, because you don't have space for it in that fixed buffer.

    Oops.

    How about objects that take modifications? E.g., a rifle that can be added a sniper scope, or a pistol which can take a silencer? Or a SMG that can take _both_? Suddenly you can't just add a linked list that points at the augmentations added, because those don't work well with the save-whole-memory-block technique.

    Etc.

    --
    A polar bear is a cartesian bear after a coordinate transform.
    1. Re:It also prevents anything from being dynamic by GreyArtist · · Score: 1

      ...writing a block of memory prevents using _any_ kind of pointer, not just virtual function table pointers. _Any_ kind of a linked list, hash table, dynamically sized array, etc...

      Unless your linked list/hash table is made from indexes into the data structure, rather than true process address space pointers. It adds some minor complexity to the referencing and dereferencing, but still might be worth it. If not, you can always just iterate.

      My question at the top of this thread had been simplified to refer to staticly-allocated static (global) variables alone, but the argument can be extended to include dynamically-allocated static (global) variables. Dynamic allocation would probably cause an additonal write function call for each allocation, but in my opinion that would still be better than iterating through the gamestate methods of 40,000 objects, each of which has to call a game-context class method (or i/o stream function) to do the actual storing.

      It's also a matter of complexity. I don't want every one of my object classes to have a saveme() method, inherited or not. It doesn't seem like it should be their reponsiblity, and they are already complex enough, the little buggers.

    2. Re:It also prevents anything from being dynamic by Moraelin · · Score: 1

      Using indexes into the same table is still not solving the problem of allowing mods, for example. As soon as the table needs to be n+1 objects instead of n, you have a problem with reading old games.

      As for the saveme() method, it actually _reduces_ complexity, and reduces the risk of bugs, since the code is close to the actual object. Instead of having one big method with 100+ if blocks that can save this if it's a map, that if it's a weapon, and that other thing if it's an armour, each kind of object can already know exactly what it can save and handle only that.

      That said, it doesn't really have to be in _every_ object. If your game objects are, for example, based on some kind of a hash-table, you only need a function that serializes hash-tables. That's one very easy method to write, and can thereafter be inherited by all objects that fit.

      The idea isn't as crazy as you might think: each game object is, after all, a collection of properties and values. For a weapon, you have range, ammo clip size, accuracy, etc. For an NPC you have stats and skills and inventory slots. For armour, similarly. For crafts (e.g., building a sword from a hilt and blade, the blade from a blank using a hammer at the anvil, etc) you need the same thing: what goes in, what tools are needed, what skill level. Spells? Ditto. Alchemy? See crafts. Etc.

      It's all properties.

      It also has the advantage that it's extensible. If in the expansion pack you decide to add a new stat or skill or feat, or anything else, you just need a new property. You can even read the old saved games, as long as the code can work when the new property doesn't exist. (E.g., assume the default value for a missing stat.)

      And by now you probably get the idea "but hash-maps are too slow for games!" Hardly the case any more, actually. See, most of the time in a game is spent in the GPU or in GPU-related stuff.

      Things like calculating the To-Hit probability, or deciding how much damage you took through armour, etc, are less than 1% of the CPU usage. It's just not worth optimizing those any more. Any properties needed for that, can jolly well be in a hash-table, and noone will notice any difference.

      Don't believe me? "The Fall" for example is scripted almost entirely in Python, including the whole combat system, skill system, etc. Using basically hash-tables. Sure, the graphics engine and pathfinding and such were still compiled stuff, but virtually all else was Python.

      Or, if you absolutely must optimize, use unique numeric indexes instead of string keys. And #define them to something more mnemonic like STAT_STR or WEAPON_ACCURACY.

      --
      A polar bear is a cartesian bear after a coordinate transform.
  85. FUCK YOU! FUCK YOU AND YOUR NIECE"S BICYCLE SEAT! by Anonymous Coward · · Score: 0

    Go fuck your mother. A good grasp of the language is fucking essential to make it far in the world. The GP was trying to be helpful. It's stupid motherfuckers like you that are flushing our language right down the fucking toilet. Don't be such a fucking shithead. Try to be more fucking professional, asshole.

  86. Premature optimization is the root of all evil by Moraelin · · Score: 1

    Keyword being: premature.

    Let me tell you a story: I've actually written game engines, in the days of Doom, before accelerated 3D graphics cards were anywhere near common. I've actually had to optimize stuff lots.

    You had to calculate and paint every pixel in a 320x200 pixel image. Assuming an overdraw factor as low as 2, that's 128,000 pixels written per frame. You want 30 frames per second? Good, that's some 3,740,000 pixels drawn per second.

    So you wrote assembly, counted cycles to optimize by hand, and unrolled loops by hand like there's no tomorrow. Yep, that was a different beast. (It was also fun.)

    But here's the other side of the coin: optimizing stuff that's _not_ the bottleneck is just plain old stupid. "Optimizing" it in a way that ties your hands in another area, is double-plus stupid.

    And "optimizing" without even measuring is downright lobotomized. _The_ worst code, including worst performing, code I've seen so far was invariably code that someone thought they "optimized" but never measured it. You'd be surprised in how many ways reality can differ from what you think as "the fastest way."

    E.g., at one point we thought that getting rid of compression for the resources (ought to speed level loading up. I mean, it's logical enough, right? You save the time to decompress those files, right?

    Turns out that it actually loaded faster with the compression in place. The CPU time saved for decompression was actually a spit in the bucket compared to the extra time used by the HDD to read the uncompressed data. Go figure.

    So to get back to the topic at hand, I find the claim _very_ suspect that saving a raw block of RAM is needed nowadays.

    It has a _ton_ of disadvantages, it's fragile, and for what? Even if we're talking coding for a console and their slow memory cards (in fact _especially_ for consoles) a well written serialization can actually write less data than blindly dumping a huge static block where you pre-allocated the largest arrays you can possibly ever need during the game.

    --
    A polar bear is a cartesian bear after a coordinate transform.
    1. Re:Premature optimization is the root of all evil by GreyArtist · · Score: 1

      Let me clarify a few things for anyone who has gotten caught up on nearly off-topic issues:

      1) I have an option I am aware of known as Option A
      2) I have an option I am aware of known as Option B
      3) I wish I had an option C (and maybe D, E, and F)
      4) Since I have neither taken Option A nor Option B, I am not in the process of optimizing anything (or even thinking about optimization), and, in fact, I haven't even completed investigating C, D, E, or F.
      5) It's a little too premature to even be premature.

  87. C++ serialization by cantao · · Score: 1

    I haven't tested this, but it seems very promising by the examples.

    http://s11n.net/

    Enjoy!

    Cantão!

  88. Real life is funnier by Chemisor · · Score: 1

    > Blindly writing a block of memory prevents using _any_ kind of pointer

    It is amusing that it will actually work for the developer for quite some time, because often the program is loaded at the same address each time you run it. Then you install some service and suddenly your saved games don't work any more :) I've never had a program that relied on this, but I have occasionally taken advantage of it when dumping data in the debugger.

    > how's a fixed memory block going to accomodate the inventory
    > in an RPG? Have a fixed buffer for every single object in the
    > game, for example, saying how much of each you've got?

    Believe it or not, that's exactly what Omega (ancient roguelike game) does; there is a 26-slot pack in a global variable and a 13-slot inventory in a global variable. It also has an Objects and Monsters global static arrays in large header files, which are modified at runtime to indicate which objects are "known" and to make special creatures.

  89. It's been a while.... by orion41us · · Score: 1

    since I've done much in OOP w/ C++ (Mostly do ASP.NET and SQL nowdays)... But can't you physicaly save a instance of an object? I seam to remember you could bit-by-bit save to a stream....

  90. Dunno about C++ by ledow · · Score: 1

    I'm not too hot on C++ but a while back I was working on a similar problem in Java. I wanted platform-independent, non-binary state saving files that could easily be extended without breaking the format and the feature of Java that saved my life was Reflection.

    With the Reflection classes, you were able to read out, programatically, a list of the variables, methods/functions etc. within each class in the currently running program.

    Properly programmed, and by overloading/extending the Serialization classes, you could look into the internals of the program that was running, view all the variables, choose which ones needed to be saved, serialise them into a nice plain-text format (INI, XML, whatever).

    If you designed the format right, you could save the info in such a way that a later version of the program would save the same info, with extra fields, in the same format and not have to worry about whether it was a version 1 or a version 2 savegame.

    I don't know if there is an equivalent in C++ (I doubt it with all those pointers and complications) but you only really see the power of a Reflection class when you start playing about with it like this.

  91. Don't take the professor verbatim by PickyH3D · · Score: 1
    There were two immediate thoughts that came to mind when I read this posting.

    First: Professors are probably trying to protect the less intelligent in the class from themselves, by attempting to force them not to use globals (since beginners can tend to over use them, which leads to jumbled, unreadable code).
    Second: A lot of programming professors do not know what they are saying. He may honestly believe that there is never a case for global variables, just as many programmers believe there is never a case for goto statements. Globally defined constants are still global. What does he say to that? "That's different?"

    Onto your post, the true object oriented method would not be to have a static, global object simply marshalling all of the data around. Yes, it is a way to do it, but the general object oriented way would be to make objects that are necessary to be saved as serializable. Loop through them all, then bam, you're done. Easier said then done, but hey, that's object oriented.

    1. Re:Don't take the professor verbatim by Anonymous Coward · · Score: 0

      Globally defined constants are still global. What does he say to that? "That's different?"

      Uh, well that *is* different. Constants don't change state at runtime (under the actual definition of "constant" anyway). The point is that you want to minimize the scope of stateful objects. That's why people say you should avoid global objects in the first place!

    2. Re:Don't take the professor verbatim by PickyH3D · · Score: 1
      They're different in terms of their definition, but that's about it. One does not change, but the other can. I know they are different, but not by much and they serve the exact same purpose, excluding state objects, which only serve to be changed (constants only gets half that game down). You're just trying to nitpick a fatal flaw in the argument against globals, which I dreadfully hate (globals, not the argument). Globals are necessary in many problems and as I agree with in another post, singletons fit the object oriented idea of global variables. So if you won't accept my idea of global constants, then what about global singletons?

      People say to avoid global variables because a lot of programmers go overboard with them. "It's used by more than one function? Make it global!" It does not fit good design practices and the problem with most variables used in this manner is that down the road they are simply changed 'because you can.' The idea of minimizing scope of global variables is rather rediculous because they are global; I assume you are referring to member variables here.

      This is the same reason that most good programmers tell new ones to avoid multiple inheritance, which is because if you do not understand it, which most do not, then it will eventually come back to bite you and that it is too easy to go overboard using it. There are quite a few good reasons to want to use multiple inheritance (for example, inheriting from a base form type and a currently defined form, such as a browser control with missing functionality), but there are also quite a few bad ones. The exact same logic follows for global variables, there are quite a few good reasons to use them, but there are also quite a few bad ones.

  92. Memory dump is usually not a ood idea by Branko · · Score: 1

    I simply save a single large segment of data that contains all the necessary information

    How do you "simply save" such a block of memory? If you are doing a simple memory dump into file, then all sorts of problems can crop-up. For example, you cannot save pointer in this manner because the next run of the same program will not necessarily have the same memory layout (i.e. even if same objects are created they may not be at the same address as the last run).
    Other problems may occur... differences in alignment (because of change in compiler options), insertion of more variables in the next version of your program, differences in how compiler stores them (because you changed the compiler or compiler version)...

    Basically, even if it works, it will be very difficult to attain any kind of portability (between program versions or platforms) for such save files.

  93. No no no no.... by Digital_Quartz · · Score: 1

    If your requirements include;
    1) The ability to save games
    2) Save games must be compact in size
    3) Saving games must be done efficiently

    Then your system architecture should take these requirements into account, and your architecture document should outline a clear and consistent scheme for saving games, and your design documentation for each module should identify which attributes need to be persistant and stored in a save game file, which do not, and which ones might optionally be saved on the PC version where disk space is a non-issue.

    You should be able to come up with a fairly accurate estimate of a save-game file's size and the time it will take to create it before you start coding, based on the other requirements of the game. If you fail to meet the required sizes for your target platforms, you know you either have to ditch those platforms, or adjust the requirements of your game.

    The method you describe is what as known as "hacking in a feature". This is how junior designers turn otherwise simple requirements into "walls". It is also how you end up spending 6 months writing code, only to re-work all of it because it "didn't work out".

    It's frequently the difference between being profitable and being a failure.

  94. Globals and saving-printing by dmh20002 · · Score: 1

    read this for an old hands view. Basically you are sacrificing most of what is known about good design practice for a marginal benefit.

    1. Re:Globals and saving-printing by GreyArtist · · Score: 1

      Wow, I don't even know where to begin...

      The author of the article, IMHO, has clearly been on the management side of the development process far too long. While providing many examples for his readers to laugh and sneer at, he provided little in the way of good design advice or practical knowledge. "Keep it simple?" Sounds like he really just wants everyone to do it his way. "Keep it simple" is the essence of every "bad design choice" I make, mainly because I program on my own dollar, not anyone elses.

  95. Die hard? by stonecypher · · Score: 1

    The idea that all die-hard OO fanatics would do the same thing is absurd. Yes, making a member method (or more desirably the stream operators) for serialization is desirable. However, one could just as easily use a generic serializer like Boost::Serialize(), or a database, or SQLite, or a traditional set of structs, or a byte stream, or feed something to bzip, or or or or or...

    Don't choose technique by dogma. Choose technique to suit the current project's need. As the commercial said, "anything else would be uncivilized."

    --
    StoneCypher is Full of BS
  96. Your missing the main point of OO by sideshow+Pablo · · Score: 1
    {Soapbox}

    First off, learning OO via C++, C# or Java will not give a very good first impression of it because they allow you to break OO rules way too easily. To see real OO in action you should check out Smalltalk via squeak http://www.squeak.org/ (it's free). Smalltalk is designed so it's hard to write anything procedurally and it's very easy to learn (something like 5 reserved words total).

    {/Soapbox}

    Second off, the benefit of OO is that it is much easier for OTHER coders to learn and modify a properly designed OO piece of code than a procedurally designed one. Trust me. One of my first tasks as a 'professional programmer' was to understand a 30 page main procedurally coded simulator. Not fun. Not at all. The guy who wrote it had no problem finding exactly where code mods had to go but when he was reassigned to another project all that knowledge went with him. When I had to learn a well designed OO program it was MUCH MUCH easier.

    Chekoff,(Sorry, I just read the Star Trek story)in summary, the difference between a good coder and a great coder is NOT that their code compiles,works, and that one can write code quicker than the other, its how well the design can be learned and modified by others. In the coding business, single points of failures aka losing contracts/business because your stud coder got hit by a bus, is bad.

    -Pablo

  97. Simple by Anonymous Coward · · Score: 0
    //before

    int var1, var2, var3;

    void save(FILE *pFile)
    {
    fwrite(&var1, sizeof(var1), 1, pFile);
    fwrite(&var2, sizeof(var1), 1, pFile);
    fwrite(&var3, sizeof(var1), 1, pFile);
    }

    void main()
    {
    FILE *pFile = fopen(argv[1], "r+b");
    save(pFile);
    }

    //after

    class game {
    int var1, var2, var3;

    public:
    void save(FILE *pFile)
    {
    fwrite(&var1, sizeof(var1), 1, pFile);
    fwrite(&var2, sizeof(var1), 1, pFile);
    fwrite(&var3, sizeof(var1), 1, pFile);
    }
    };

    void main()
    {
    FILE *pFile = fopen(argv[1], "r+b");

    game TheGame;
    TheGame.save(pFile);
    }
  98. Here's a free one I wrote... by DrCode · · Score: 1

    CSer

    Personally, I've moved away from use of C++ serialization, and prefer to write custom "save" code. It may take longer, but it's easier to debug and to maintain upward compatibility.

  99. Re:FUCK YOU! FUCK YOU AND YOUR NIECE"S BICYCLE SEA by Anonymous Coward · · Score: 0

    well you certainly have a grasp of the language (where "language" == your dick, wanking furiously).

  100. Give it time by Anonymous Coward · · Score: 0

    Add twenty devlopers working 60 hours a week. Spawn a few threads in your game. And see how well your global data holds up over the course of several game releases.
    You don't have a counter argument for the use of global data. You have in experience.