Thursday, 31 December 2009

iPhone - Serialize this

Just implemented saving and loading. It's really really really simple to do.

The basic concept is the following.


// Grab the default archive

NSUserDefaults *archive = [NSUserDefaults standardUserDefaults];


// Create a magic key for the value you're about to load or save

NSString *key = [[NSString alloc] initWithFormat:@"myData"];


// To save

[archive setFloat:vector->x forKey:vectorKeyX];


// To load

vector->x = [archive floatForKey:vectorKeyX];


// Don't forget to release the key

[key release];


Armed with these tools I went with the approach to save out as much of the game state required to re-launch the game back into same state. To do this I set up the engine to process a number of scenes. Each scene is responsible for a number of objects. When I save out the game data, I go through all the objects and save out what scene they're in, so on launch, I reload all the scenes that were currently loaded, which will then create all the required objects. I then pass through all the objects and load in the required data to get them working the way they were.

I organised my objects to create unique keys depending on their object index and the internal data index I was processing. Enough talk, here's the code.

// Creates the unique key for our object

+(NSString*)getSerializeKey:(uint)index key:(NSString*)key

{

if( key == nil )

{

return [[NSString alloc] initWithFormat:@"Obj%i", index];

}

else

{

return [[NSString alloc] initWithFormat:@"[email protected]%i", key, index];

}

}


// Creates our key and passes it into the serializeInternals function for saving the internal data

-(void)serialize:(NSUserDefaults*)archive saving:(BOOL)saving key:(NSString*)key index:(uint)index

{

NSString *objectKey = [ObjectBase getSerializeKey:index key:(NSString*)key];

// The internal index will aid the creation of unique keys for our internal data

uint internalIndex = 0;

[self serializeInternals:archive saving:saving key:key index:&internalIndex];

[objectKey release];

}


// Overridden by child classes to save more data

-(void)serializeInternals:(NSUserDefaults*)archive saving:(BOOL)saving key:(NSString*)key index:(uint*)index

{

// Save our object position by default

[self serializeVector:&position archive:archive saving:saving key:key index:index];

}


// Save/load our scenes with a unique scene key

+(void)serializeScene:(SceneBase*)scene archive:(NSUserDefaults*)archive saving:(BOOL)saving key:(NSString*)key

{

NSString *sceneKey = [[NSString alloc] initWithFormat:@"[email protected]", key];

if( saving )

{

[archive setInteger:scene ? scene->index : -1 forKey:sceneKey];

}

else

{

NSInteger sceneIndex = [archive integerForKey:sceneKey];

[gEngine addScene:sceneIndex];

}

[sceneKey release];

}


// The keys for the objects internal vectors are concatenated by the object key and the internal index key

-(void)serializeVector:(Vector3*)vector archive:(NSUserDefaults*)archive saving:(BOOL)saving key:(NSString*)key index:(uint*)index

{

NSString *vectorKeyX = [[NSString alloc] initWithFormat:@"[email protected]%iX", key, *index];

NSString *vectorKeyY = [[NSString alloc] initWithFormat:@"[email protected]%iY", key, *index];

NSString *vectorKeyZ = [[NSString alloc] initWithFormat:@"[email protected]%iZ", key, *index];

if( saving )

{

[archive setFloat:vector->x forKey:vectorKeyX];

[archive setFloat:vector->y forKey:vectorKeyY];

[archive setFloat:vector->z forKey:vectorKeyZ];

}

else

{

vector->x = [archive floatForKey:vectorKeyX];

vector->y = [archive floatForKey:vectorKeyY];

vector->z = [archive floatForKey:vectorKeyZ];

}

[vectorKeyX release];

[vectorKeyY release];

[vectorKeyZ release];

(*index)++;

}


That's the basic jist of it, create unique keys for saving and loading the data.