所以我目前有一种为我的 Cocos2d 游戏保存设置的有效方法,我使用的方法是 XML 解析器。
首先,将 NSUserDefaults 用于这样的事情会更好吗?
编辑:如果您已经知道 XML 解析器的工作原理,请跳到最后。
这是我的 GlobalSettings.h:
#import <Foundation/Foundation.h>
@interface GlobalSettings : NSObject {
// Declare variables with an underscore in front, for example:
int _currency;
BOOL _BankerBossDefeated;
BOOL _BabyBossDefeated;
BOOL _DuckBossDefeated;
BOOL _BaseBallBossDefeated;
BOOL _NewtonBossDefeated;
BOOL _CatchExtender;
BOOL _CatchExtenderEnabled;
BOOL _SpeedBoost;
BOOL _SpeedBoostEnabled;
}
// Declare your variable properties without an underscore, for example:
@property (nonatomic, assign) int currency;
@property (nonatomic, assign) BOOL BankerBossDefeated;
@property (nonatomic, assign) BOOL BabyBossDefeated;
@property (nonatomic, assign) BOOL DuckBossDefeated;
@property (nonatomic, assign) BOOL BaseBallBossDefeated;
@property (nonatomic, assign) BOOL NewtonBossDefeated;
@property (nonatomic, assign) BOOL SpeedBoost;
@property (nonatomic, assign) BOOL CatchExtender;
@property (nonatomic, assign) BOOL SpeedBoostEnabled;
@property (nonatomic, assign) BOOL CatchExtenderEnabled;
// Put your custom init method interface here:
-(id)initWithcurrency:(int)currency
BankerBossDefeated:(BOOL)BankerBossDefeated
BabyBossDefeated:(BOOL)BabyBossDefeated
DuckBossDefeated:(BOOL)DuckBossDefeated
BaseBallBossDefeated:(BOOL)BaseBallBossDefeated
NewtonBossDefeated:(BOOL)NewtonBossDefeated
CatchExtender:(BOOL)CatchExtender
SpeedBoost:(BOOL)SpeedBoost
CatchExtenderEnabled:(BOOL)CatchExtenderEnabled
SpeedBoostEnabled:(BOOL)SpeedBoostEnabled;
@end
我的 GlobalSettings.m 是:
#import "GlobalSettings.h"
@implementation GlobalSettings
// Synthesize your variables here, for example:
@synthesize currency = _currency;
@synthesize BankerBossDefeated = _BankerBossDefeated;
@synthesize BabyBossDefeated = _BabyBossDefeated;
@synthesize DuckBossDefeated = _DuckBossDefeated;
@synthesize BaseBallBossDefeated = _BaseBallBossDefeated;
@synthesize NewtonBossDefeated = _NewtonBossDefeated;
@synthesize SpeedBoost = _SpeedBoost;
@synthesize CatchExtender = _CatchExtender;
@synthesize SpeedBoostEnabled = _SpeedBoostEnabled;
@synthesize CatchExtenderEnabled = _CatchExtenderEnabled;
// put your custom init method here which takes a variable
// for each class instance variable
-(id)initWithcurrency:(int)currency
BankerBossDefeated:(BOOL)BankerBossDefeated
BabyBossDefeated:(BOOL)BabyBossDefeated
DuckBossDefeated:(BOOL)DuckBossDefeated
BaseBallBossDefeated:(BOOL)BaseBallBossDefeated
NewtonBossDefeated:(BOOL)NewtonBossDefeated
CatchExtender:(BOOL)CatchExtender
SpeedBoost:(BOOL)SpeedBoost
CatchExtenderEnabled:(BOOL)CatchExtenderEnabled
SpeedBoostEnabled:(BOOL)SpeedBoostEnabled;{
if ((self = [super init])) {
// Set class instance variables based on values
// given to this method
self.currency = currency;
self.BankerBossDefeated = BankerBossDefeated;
self.BabyBossDefeated = BabyBossDefeated;
self.DuckBossDefeated = DuckBossDefeated;
self.BaseBallBossDefeated = BaseBallBossDefeated;
self.NewtonBossDefeated = NewtonBossDefeated;
self.CatchExtender = CatchExtender;
self.SpeedBoost = SpeedBoost;
self.CatchExtenderEnabled = CatchExtenderEnabled;
self.SpeedBoostEnabled = SpeedBoostEnabled;
}
return self;
}
- (void) dealloc {
[super dealloc];
}
@end
然后我用 SettingsParser.h 解析 XML:
#import <Foundation/Foundation.h>
@class GlobalSettings;
@interface SettingsParser : NSObject {}
+ (GlobalSettings *)loadData;
+ (void)saveData:(GlobalSettings *)saveData;
@end
和 SettingsParser.m:
#import "SettingsParser.h"
#import "GlobalSettings.h"
#import "GDataXMLNode.h"
@implementation SettingsParser
+ (NSString *)dataFilePath:(BOOL)forSave {
NSString *xmlFileName = @"GlobalSettings";
/***************************************************************************
This method is used to set up the specified xml for reading/writing.
Specify the name of the XML file you want to work with above.
You don't have to worry about the rest of the code in this method.
***************************************************************************/
NSString *xmlFileNameWithExtension = [NSString stringWithFormat:@"%@.xml",xmlFileName];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *documentsPath = [documentsDirectory stringByAppendingPathComponent:xmlFileNameWithExtension];
if (forSave || [[NSFileManager defaultManager] fileExistsAtPath:documentsPath]) {
return documentsPath;
NSLog(@"%@ opened for read/write",documentsPath);
} else {
NSLog(@"Created/copied in default %@",xmlFileNameWithExtension);
return [[NSBundle mainBundle] pathForResource:xmlFileName ofType:@"xml"];
}
}
+ (GlobalSettings *)loadData {
/***************************************************************************
This loadData method is used to load data from the xml file
specified in the dataFilePath method above.
MODIFY the list of variables below which will be used to create
and return an instance of TemplateData at the end of this method.
***************************************************************************/
int currency;
BOOL BankerBossDefeated;
BOOL BabyBossDefeated;
BOOL DuckBossDefeated;
BOOL BaseBallBossDefeated;
BOOL NewtonBossDefeated;
BOOL CatchExtender;
BOOL SpeedBoost;
BOOL CatchExtenderEnabled;
BOOL SpeedBoostEnabled;
// Create NSData instance from xml in filePath
NSString *filePath = [self dataFilePath:FALSE];
NSData *xmlData = [[NSMutableData alloc] initWithContentsOfFile:filePath];
NSError *error;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData options:0 error:&error];
if (doc == nil) { return nil; NSLog(@"xml file is empty!");}
NSLog(@"Loading %@", filePath);
/***************************************************************************
This next line will usually have the most customisation applied because
it will be a direct reflection of what you want out of the XML file.
***************************************************************************/
NSArray *dataArray = [doc nodesForXPath:@"//GlobalSettings" error:nil];
NSLog(@"Array Contents = %@", dataArray);
/***************************************************************************
We use dataArray to populate variables created at the start of this
method. For each variable you will need to:
1. Create an array based on the elements in the xml
2. Assign the variable a value based on data in elements in the xml
***************************************************************************/
for (GDataXMLElement *element in dataArray) {
NSArray *currencyArray = [element elementsForName:@"currency"];
NSArray *BankerBossDefeatedArray = [element elementsForName:@"BankerBossDefeated"];
NSArray *BabyBossDefeatedArray = [element elementsForName:@"BabyBossDefeated"];
NSArray *DuckBossDefeatedArray = [element elementsForName:@"DuckBossDefeated"];
NSArray *BaseBallBossDefeatedArray = [element elementsForName:@"BaseBallBossDefeated"];
NSArray *NewtonBossDefeatedArray = [element elementsForName:@"NewtonBossDefeated"];
NSArray *CatchExtenderArray = [element elementsForName:@"CatchExtender"];
NSArray *SpeedBoostArray = [element elementsForName:@"SpeedBoost"];
NSArray *CatchExtenderEnabledArray = [element elementsForName:@"CatchExtenderEnabled"];
NSArray *SpeedBoostEnabledArray = [element elementsForName:@"SpeedBoostEnabled"];
// currency
if (currencyArray.count > 0) {
GDataXMLElement *currencyElement = (GDataXMLElement *) [currencyArray objectAtIndex:0];
currency = [[currencyElement stringValue] intValue];
}
// BankerBossDefeated
if (BankerBossDefeatedArray.count > 0) {
GDataXMLElement *BankerBossDefeatedElement = (GDataXMLElement *) [BankerBossDefeatedArray objectAtIndex:0];
BankerBossDefeated = [[BankerBossDefeatedElement stringValue] boolValue];
}
// DuckBossDefeated
if (DuckBossDefeatedArray.count > 0) {
GDataXMLElement *DuckBossDefeatedElement = (GDataXMLElement *) [DuckBossDefeatedArray objectAtIndex:0];
DuckBossDefeated = [[DuckBossDefeatedElement stringValue] boolValue];
}
// BabyBossDefeated
if (BabyBossDefeatedArray.count > 0) {
GDataXMLElement *BabyBossDefeatedElement = (GDataXMLElement *) [BabyBossDefeatedArray objectAtIndex:0];
BabyBossDefeated = [[BabyBossDefeatedElement stringValue] boolValue];
}
// BaseBallBossDefeated
if (BaseBallBossDefeatedArray.count > 0) {
GDataXMLElement *BaseBallBossDefeatedElement = (GDataXMLElement *) [BaseBallBossDefeatedArray objectAtIndex:0];
BaseBallBossDefeated = [[BaseBallBossDefeatedElement stringValue] boolValue];
}
// NewtonBossDefeated
if (NewtonBossDefeatedArray.count > 0) {
GDataXMLElement *NewtonBossDefeatedElement = (GDataXMLElement *) [NewtonBossDefeatedArray objectAtIndex:0];
NewtonBossDefeated = [[NewtonBossDefeatedElement stringValue] boolValue];
}
// CatchExtender
if (CatchExtenderArray.count > 0) {
GDataXMLElement *CatchExtenderElement = (GDataXMLElement *) [CatchExtenderArray objectAtIndex:0];
CatchExtender = [[CatchExtenderElement stringValue] boolValue];
}
// SpeedBoost
if (SpeedBoostArray.count > 0) {
GDataXMLElement *SpeedBoostElement = (GDataXMLElement *) [SpeedBoostArray objectAtIndex:0];
SpeedBoost = [[SpeedBoostElement stringValue] boolValue];
}
// CatchExtenderEnabled
if (CatchExtenderEnabledArray.count > 0) {
GDataXMLElement *CatchExtenderEnabledElement = (GDataXMLElement *) [CatchExtenderEnabledArray objectAtIndex:0];
CatchExtenderEnabled = [[CatchExtenderEnabledElement stringValue] boolValue];
}
// SpeedBoost
if (SpeedBoostEnabledArray.count > 0) {
GDataXMLElement *SpeedBoostEnabledElement = (GDataXMLElement *) [SpeedBoostEnabledArray objectAtIndex:0];
SpeedBoostEnabled = [[SpeedBoostEnabledElement stringValue] boolValue];
}
}
/***************************************************************************
Now the variables are populated from xml data we create an instance of
TemplateData to pass back to whatever called this method.
The initWithExampleInt:exampleBool:exampleString will need to be replaced
with whatever method you have updaed in the TemplateData class.
***************************************************************************/
//NSLog(@"XML value read for exampleInt = %i", exampleInt);
//NSLog(@"XML value read for exampleBool = %i", exampleBool);
//NSLog(@"XML value read for exampleString = %@", exampleString);
GlobalSettings *Data = [[GlobalSettings alloc] initWithcurrency:currency
BankerBossDefeated:BankerBossDefeated
BabyBossDefeated:BabyBossDefeated DuckBossDefeated:DuckBossDefeated BaseBallBossDefeated:BaseBallBossDefeated NewtonBossDefeated:NewtonBossDefeated
CatchExtender:CatchExtender
SpeedBoost:SpeedBoost CatchExtenderEnabled:CatchExtenderEnabled
SpeedBoostEnabled:SpeedBoostEnabled];
[doc release];
[xmlData release];
return Data;
[Data release];
}
+ (void)saveData:(GlobalSettings *)saveData {
/***************************************************************************
This method writes data to the xml based on a TemplateData instance
You will have to be very aware of the intended xml contents and structure
as you will be wiping and re-writing the whole xml file.
We write an xml by creating elements and adding 'children' to them.
You'll need to write a line for each element to build the hierarchy // <-- MODIFY CODE ACCORDINGLY
***************************************************************************/
GDataXMLElement *GlobalSettingsElement = [GDataXMLNode elementWithName:@"GlobalSettings"];
GDataXMLElement *currencyElement = [GDataXMLNode elementWithName:@"currency"
stringValue:[[NSNumber numberWithInt:saveData.currency] stringValue]];
GDataXMLElement *BankerBossDefeatedElement = [GDataXMLNode elementWithName:@"BankerBossDefeated"
stringValue:[[NSNumber numberWithBool:saveData.BankerBossDefeated] stringValue]];
GDataXMLElement *BabyBossDefeatedElement = [GDataXMLNode elementWithName:@"BabyBossDefeated"
stringValue:[[NSNumber numberWithBool:saveData.BabyBossDefeated] stringValue]];
GDataXMLElement *DuckBossDefeatedElement = [GDataXMLNode elementWithName:@"DuckBossDefeated"
stringValue:[[NSNumber numberWithBool:saveData.DuckBossDefeated] stringValue]];
GDataXMLElement *BaseBallBossDefeatedElement = [GDataXMLNode elementWithName:@"BaseBallBossDefeated"
stringValue:[[NSNumber numberWithBool:saveData.BaseBallBossDefeated] stringValue]];
GDataXMLElement *NewtonBossDefeatedElement = [GDataXMLNode elementWithName:@"NewtonBossDefeated"
stringValue:[[NSNumber numberWithBool:saveData.NewtonBossDefeated] stringValue]];
GDataXMLElement *CatchExtenderElement = [GDataXMLNode elementWithName:@"CatchExtender"
stringValue:[[NSNumber numberWithBool:saveData.CatchExtender] stringValue]];
GDataXMLElement *SpeedBoostElement = [GDataXMLNode elementWithName:@"SpeedBoost"
stringValue:[[NSNumber numberWithBool:saveData.SpeedBoost] stringValue]];
GDataXMLElement *CatchExtenderEnabledElement = [GDataXMLNode elementWithName:@"CatchExtenderEnabled"
stringValue:[[NSNumber numberWithBool:saveData.CatchExtender] stringValue]];
GDataXMLElement *SpeedBoostEnabledElement = [GDataXMLNode elementWithName:@"SpeedBoostEnabled"
stringValue:[[NSNumber numberWithBool:saveData.SpeedBoost] stringValue]];
// Using the elements just created, set up the hierarchy
[GlobalSettingsElement addChild:currencyElement];
[GlobalSettingsElement addChild:BankerBossDefeatedElement];
[GlobalSettingsElement addChild:BabyBossDefeatedElement];
[GlobalSettingsElement addChild:DuckBossDefeatedElement];
[GlobalSettingsElement addChild:BaseBallBossDefeatedElement];
[GlobalSettingsElement addChild:NewtonBossDefeatedElement];
[GlobalSettingsElement addChild:CatchExtenderElement];
[GlobalSettingsElement addChild:SpeedBoostElement];
[GlobalSettingsElement addChild:CatchExtenderEnabledElement];
[GlobalSettingsElement addChild:SpeedBoostEnabledElement];
GDataXMLDocument *document = [[[GDataXMLDocument alloc]
initWithRootElement:GlobalSettingsElement] autorelease];
NSData *xmlData = document.XMLData;
NSString *filePath = [self dataFilePath:TRUE];
NSLog(@"Saving data to %@...", filePath);
[xmlData writeToFile:filePath atomically:YES];
}
@end
编辑:实际问题从这里开始:
在我的菜单类中,当 CatchExtender 和 SpeedBoost 启用时,我有两个开关(它们是在游戏商店中购买的)。在这些开关中,我想根据开关设置 SpeedBoostEnabled 和 CatchExtenderEnabled。
这些是开关:
在我的初始化中:
if (GlobalSettings.CatchExtender == TRUE) {
if(GlobalSettings.SpeedBoost == TRUE){
catchSwitch = [[ UISwitch alloc ] initWithFrame: CGRectMake(220, 400, 10, 10)];
catchSwitch.center = CGPointMake(240, 450);
CCLabelTTF *catchLabel = [CCLabelTTF labelWithString:@"Catch Extender" fontName:@"Chalkduster" fontSize:15];
catchLabel.color = ccWHITE;
catchLabel.position = CGPointMake(240, 60);
[self addChild: catchLabel];
}else{
catchSwitch = [[ UISwitch alloc ] initWithFrame: CGRectMake(160, 400, 10, 10)];
catchSwitch.center = CGPointMake(160, 450);
CCLabelTTF *catchLabel = [CCLabelTTF labelWithString:@"Catch Extender" fontName:@"Chalkduster" fontSize:15];
catchLabel.color = ccWHITE;
catchLabel.position = CGPointMake(160, 60);
[self addChild: catchLabel];
}
catchSwitch.on = NO; //set to be OFF at start
catchSwitch.tag = 1; // this is not necessary - only to find later
[catchSwitch addTarget:self action:@selector(catchAction:) forControlEvents:UIControlEventValueChanged];
[[[CCDirector sharedDirector] openGLView] addSubview:catchSwitch];
}
if (GlobalSettings.SpeedBoost == TRUE) {
if(GlobalSettings.CatchExtender == TRUE){
speedSwitch = [[ UISwitch alloc ] initWithFrame: CGRectMake(100, 400, 10, 10)];
speedSwitch.center = CGPointMake(80, 450);
CCLabelTTF *speedLabel = [CCLabelTTF labelWithString:@"Speed Enhancer" fontName:@"Chalkduster" fontSize:15];
speedLabel.color = ccWHITE;
speedLabel.position = CGPointMake(80, 60);
[self addChild: speedLabel];
}else{
speedSwitch = [[ UISwitch alloc ] initWithFrame: CGRectMake(160, 400, 10, 10)];
speedSwitch.center = CGPointMake(160, 450);
}
speedSwitch.on = NO; //set to be OFF at start
speedSwitch.tag = 1; // this is not necessary - only to find later
[speedSwitch addTarget:self action:@selector(speedAction:) forControlEvents:UIControlEventValueChanged];
[[[CCDirector sharedDirector] openGLView] addSubview:speedSwitch];
}
在行动中:
- (void)catchAction:(id)sender
{
// Your logic when the switch it used
// NSLog(@"switchAction: value = %d", [sender isOn]);
if ([sender isOn]) {
GlobalSettings *GlobalSettings = [SettingsParser loadData];
GlobalSettings.CatchExtenderEnabled = TRUE;
UIAlertView* dialog = [[UIAlertView alloc] init];
[dialog setDelegate:self];
[dialog setTitle:@"ON"];
[dialog setMessage:@"Catch Extender is on"];
[dialog addButtonWithTitle:@"Sweet!"];
[dialog show];
[dialog release];
[SettingsParser saveData:GlobalSettings];
}else{
GlobalSettings *GlobalSettings = [SettingsParser loadData];
GlobalSettings.CatchExtenderEnabled = FALSE;
UIAlertView* dialog = [[UIAlertView alloc] init];
[dialog setDelegate:self];
[dialog setTitle:@"OFF"];
[dialog setMessage:@"Catch Extender is off"];
[dialog addButtonWithTitle:@"Thanks"];
[dialog show];
[dialog release];
[SettingsParser saveData:GlobalSettings];
}
}
- (void)speedAction:(id)sender
{
// Your logic when the switch it used
// NSLog(@"switchAction: value = %d", [sender isOn]);
if ([sender isOn]) {
GlobalSettings *GlobalSettings = [SettingsParser loadData];
GlobalSettings.SpeedBoostEnabled = TRUE;
UIAlertView* dialog = [[UIAlertView alloc] init];
[dialog setDelegate:self];
[dialog setTitle:@"ON"];
[dialog setMessage:@"Speed Enhancer is on"];
[dialog addButtonWithTitle:@"Sweet!"];
[dialog show];
[dialog release];
[SettingsParser saveData:GlobalSettings];
}else{
GlobalSettings *GlobalSettings = [SettingsParser loadData];
GlobalSettings.SpeedBoostEnabled = FALSE;
UIAlertView* dialog = [[UIAlertView alloc] init];
[dialog setDelegate:self];
[dialog setTitle:@"OFF"];
[dialog setMessage:@"Speed Enhancer is off"];
[dialog addButtonWithTitle:@"Thanks"];
[dialog show];
[dialog release];
[SettingsParser saveData:GlobalSettings];
}
}
在保存游戏关卡数据的类中,我通常使用一个简单的 if 语句检查这些布尔值。但它似乎不起作用,设置似乎没有保存,因为在日志中,XML 文件中的值似乎没有改变..
再次对长篇文章感到抱歉,但这个问题有点困扰我。