How would you store your game state at exit for an iPhone game written in Objective-C?
Here is the method I used in my games.
First, every object that needs to be persisted must implement the NSCoding protocol. You want to only store your model data and nothing specific to the current process. This means that you can't persist pointers or any resource ids that the OS gives you at runtime. For the pointers you can fix this easily by just making sure that you encode the objects that they point to instead of the pointers themselves. For other resources, you will need a way to hook up your object to the new resource at runtime.
Second, make sure that all of your game objects can be accessed through a single root object. This object could be a master object for the entire game state, for example. Another possibility is that you could store them in one of the Foundation collection classes (NSMutableArray, NSMutableSet, NSMutableDictionary).
When you get notified that your application is moving into the background (applicationDidEnterBackground), you will need to use NSKeyedArchiver to save the entire state to a file. That file should be in the documents directory for your application. Here is a bit of code to show how that is done:
NSArray *docDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docsPath = [docDirectories objectAtIndex:0]; NSString *saveFile = [docsPath stringByAppendingPathComponent:SAVE_STATE_FILENAME]; [NSKeyedArchiver archiveRootObject:gameState toFile:saveFile;
When you detect that you have gone back to the foreground, you should remove the save file to prevent any confusion the next time your application is launched.
On application start up, you need to check for the existence of the save file. If you have one, you load it up like this:
NSArray *docDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docsPath = [docDirectories objectAtIndex:0]; NSString *saveFile = [docsPath stringByAppendingPathComponent:SAVE_STATE_FILENAME]; [gameState release]; gameState = [[NSKeyedArchiver unarchiveObjectWithFile:saveFile] retain];
Depending on your design, you may need to walk through the game state and re-connect up any resources that you could not persist. This really depends on how cleanly separated your model code is from your rendering code.
At this point, you may want to put your game into a paused state, depending on what kind of game you are making.
Hopefully this helps someone else out there who is trying to implement game saving on the iPhone.
How to store game state is a complex question that's heavily dependent on how you set up your game itself.
On the iPhone specifically, you would just have to hook into
-(void)applicationWillTerminate: in your
UIApplicationDelegate to catch when the application will exit, from user action or otherwise. You have a short amount of time to do your work before the OS will kill your process, though.
That would largely depend on how your game was coded. Are you keeping track of a few ivars or something more substantial?
If it's just a few ivars, I'd probably write them to a plist on background and load that upon startup.
If there's more ivars, you might be better off with CoreData (and/or saving their values as they change instead of trying to fit everything into the close window).
The same way you save it when the user bumps a save-game without exiting.
Is this question about how to save game state? Or how to do something at app-exit?
For the latter, the answer is: appWillTerminate (or appWillResignActive.) In iOS4 or later, you can request extra time (Google "iOS-4 request extra time") if your save-game takes a while.
If you're asking how to save game-state at all, I'm a big fan of NSDictionary to store values easily re-read by the game-engine on continue.
NSMutableDictionary *saveDict = [NSMutableDictionary dictionary]; [saveDict setValue: [NSNumber numberWithInt: currentScore] forKey: @"currentScore"]; // etc... [saveDict writeToFile: saveFilePath atomically: YES];
If you want, you can add a "signature" (i.e., like md5 or similar) to verify on game continue to make sure that someone hasn't mucked with the file to try to cheat.
I would say don't. Save it when it makes sense for the game, not when the app is exiting. For example, if this is a turn based game, then save at the end of each turn. If it's a level based game, save at the end of each level. There a couple of reasons for this:
- Mobile usage is very unpredictable. Yes, you get notified by the iPhone SDK when the app is closing, but it could be in the middle of anything, and the transition is not just "exiting" it is also receiving a call, getting a text, etc.
- You have very limited time and resources to shut your game down on app exit. Most of that will probably be consumed just with pausing your game and cleaning up memory. No need to add to that load by saving game state.
- Most games have natural pauses that make sense for saving. You can take advantage of these pauses to seamlessly store game state, and the user will never notice that you are consuming extra resources.
- By saving incrementally during game play, the amount of data to be saved will be smaller per save.
Here's an example of how to implement the NSCoding protocol for some example "map" and "player" classes:
Then you can save the objects using Dennis' NSKeyedArchiver method.