état du jeu singleton cocos2d, initWithEncoder retourne toujours null
-
28-09-2019 - |
Question
Je suis en train d'écrire un « état de jeu » test de base singleton dans cocos2d, mais pour une raison quelconque lors du chargement de l'application, initWithCoder est jamais appelé. Toute aide serait très apprécié, merci.
Voici mon singleton GameState.h:
#import "cocos2d.h"
@interface GameState : NSObject <NSCoding>
{
NSInteger level, score;
Boolean seenInstructions;
}
@property (readwrite) NSInteger level;
@property (readwrite) NSInteger score;
@property (readwrite) Boolean seenInstructions;
+(GameState *) sharedState;
+(void) loadState;
+(void) saveState;
@end
... et GameState.m:
#import "GameState.h"
#import "Constants.h"
@implementation GameState
static GameState *sharedState = nil;
@synthesize level, score, seenInstructions;
-(void)dealloc {
[super dealloc];
}
-(id)init {
if(!(self = [super init]))
return nil;
level = 1;
score = 0;
seenInstructions = NO;
return self;
}
+(void)loadState {
@synchronized([GameState class]) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *saveFile = [documentsDirectory stringByAppendingPathComponent:kSaveFileName];
Boolean saveFileExists = [[NSFileManager defaultManager] fileExistsAtPath:saveFile];
if(!sharedState) {
sharedState = [GameState sharedState];
}
if(saveFileExists == YES) {
[sharedState release];
sharedState = [[NSKeyedUnarchiver unarchiveObjectWithFile:saveFile] retain];
}
// at this point, sharedState is null, saveFileExists is 1
if(sharedState == nil) {
// this always occurs
CCLOG(@"Couldn't load game state, so initialized with defaults");
sharedState = [self sharedState];
}
}
}
+(void)saveState {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *saveFile = [documentsDirectory stringByAppendingPathComponent:kSaveFileName];
[NSKeyedArchiver archiveRootObject:[GameState sharedState] toFile:saveFile];
}
+(GameState *)sharedState {
@synchronized([GameState class]) {
if(!sharedState) {
[[GameState alloc] init];
}
return sharedState;
}
return nil;
}
+(id)alloc {
@synchronized([GameState class]) {
NSAssert(sharedState == nil, @"Attempted to allocate a second instance of a singleton.");
sharedState = [super alloc];
return sharedState;
}
return nil;
}
+(id)allocWithZone:(NSZone *)zone
{
@synchronized([GameState class]) {
if(!sharedState) {
sharedState = [super allocWithZone:zone];
return sharedState;
}
}
return nil;
}
...
-(void)encodeWithCoder:(NSCoder *)coder {
[coder encodeInt:level forKey:@"level"];
[coder encodeInt:score forKey:@"score"];
[coder encodeBool:seenInstructions forKey:@"seenInstructions"];
}
-(id)initWithCoder:(NSCoder *)coder {
CCLOG(@"initWithCoder called");
self = [super init];
if(self != nil) {
CCLOG(@"initWithCoder self exists");
level = [coder decodeIntForKey:@"level"];
score = [coder decodeIntForKey:@"score"];
seenInstructions = [coder decodeBoolForKey:@"seenInstructions"];
}
return self;
}
@end
... Je sauve l'état à la sortie de l'application, comme ceci:
- (void)applicationWillTerminate:(UIApplication *)application {
[GameState saveState];
[[CCDirector sharedDirector] end];
}
... et charger l'état lorsque l'application se termine le chargement, comme ceci:
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[GameState loadState];
...
}
J'ai essayé se déplacer où j'appelle loadState aussi, par exemple, dans ma principale CCScene, mais cela ne semble pas fonctionner non plus.
Merci encore à l'avance.
La solution
Justes! Je pense que je compris. De plus, je trouve une belle macro gain de temps pour démarrer: http: //cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html
Et la macro modifiée J'utilise: http: // GitHub. com / taberrr / Objective-C optimisé-Singleton.git (I like "sharedGameState" sur "sharedInstance")
Espérons que cela aide quelqu'un d'autre d'essayer de faire la même chose ... voici mon travail NSCoder GameState singleton:
GameState.h:
#import "SynthesizeSingleton.h"
#import "cocos2d.h"
@interface GameState : NSObject <NSCoding>
{
NSInteger level, score;
Boolean seenInstructions;
}
@property (readwrite) NSInteger level;
@property (readwrite) NSInteger score;
@property (readwrite) Boolean seenInstructions;
SYNTHESIZE_SINGLETON_FOR_CLASS_HEADER(GameState);
+(void)loadState;
+(void)saveState;
@end
GameState.m:
#import "SynthesizeSingleton.h"
#import "GameState.h"
#import "Constants.h"
@implementation GameState
@synthesize level, score, seenInstructions;
SYNTHESIZE_SINGLETON_FOR_CLASS(GameState);
- (id)init {
if((self = [super init])) {
self.level = 1;
self.score = 0;
self.seenInstructions = NO;
}
return self;
}
+(void)loadState
{
@synchronized([GameState class]) {
// just in case loadState is called before GameState inits
if(!sharedGameState)
[GameState sharedGameState];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *file = [documentsDirectory stringByAppendingPathComponent:kSaveFileName];
Boolean saveFileExists = [[NSFileManager defaultManager] fileExistsAtPath:file];
if(saveFileExists) {
// don't need to set the result to anything here since we're just getting initwithCoder to be called.
// if you try to overwrite sharedGameState here, an assert will be thrown.
[NSKeyedUnarchiver unarchiveObjectWithFile:file];
}
}
}
+(void)saveState
{
@synchronized([GameState class]) {
GameState *state = [GameState sharedGameState];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *saveFile = [documentsDirectory stringByAppendingPathComponent:kSaveFileName];
[NSKeyedArchiver archiveRootObject:state toFile:saveFile];
}
}
#pragma mark -
#pragma mark NSCoding Protocol Methods
-(void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeInt:self.level forKey:@"level"];
[coder encodeInt:self.score forKey:@"score"];
[coder encodeBool:self.seenInstructions forKey:@"seenInstructions"];
}
-(id)initWithCoder:(NSCoder *)coder
{
self = [super init];
if(self != nil) {
self.level = [coder decodeIntForKey:@"level"];
self.score = [coder decodeIntForKey:@"score"];
self.seenInstructions = [coder decodeBoolForKey:@"seenInstructions"];
}
return self;
}
@end
Enregistrement:
- (void)applicationWillTerminate:(UIApplication *)application {
...
[GameState saveState];
...
}
Chargement:
// somewhere in your app, maybe in applicationDidFinishLaunching
GameState *state = [GameState sharedGameState];
NSLog(@"sharedGameState: %@", state);
[GameState loadState];
Si quelqu'un voit des problèmes avec cela, S'IL VOUS PLAÎT parler. :)
Il semble fonctionner très bien, cependant.
Autres conseils
Vous n'avez pas besoin de télécharger la macro modifiée. Le allocWithZone original retourné nul. Il suffit de corriger l'original comme ceci:
à partir de:
+ (id)allocWithZone:(NSZone *)zone \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [super allocWithZone:zone]; \
return shared##classname; \
} \
} \
\
return nil; \
} \
à:
+ (id)allocWithZone:(NSZone *)zone \
{ \
@synchronized(self) \
{ \
if (shared##classname == nil) \
{ \
shared##classname = [super allocWithZone:zone]; \
} \
} \
\
return shared##classname; \
} \