游戏状态单例 cocos2d,initWithEncoder 总是返回 null
Posted
技术标签:
【中文标题】游戏状态单例 cocos2d,initWithEncoder 总是返回 null【英文标题】:game state singleton cocos2d, initWithEncoder always returns null 【发布时间】:2010-04-19 20:56:12 【问题描述】:我正在尝试在 cocos2d 中编写一个基本的测试“游戏状态”单例,但由于某种原因,在加载应用程序时,从未调用过 initWithCoder。任何帮助将不胜感激,谢谢。
这是我的单例 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
... 和 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
...我正在保存应用退出时的状态,如下所示:
- (void)applicationWillTerminate:(UIApplication *)application
[GameState saveState];
[[CCDirector sharedDirector] end];
...并在应用完成加载时加载状态,如下所示:
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
...
[GameState loadState];
...
我也尝试过在我调用 loadState 的地方移动,例如在我的主 CCScene 中,但这似乎也不起作用。
再次提前致谢。
【问题讨论】:
【参考方案1】:正义!我想我想通了。另外,我发现了一个不错的启动宏:http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html
我正在使用修改后的宏:http://github.com/taberrr/Objective-C-Optimized-Singleton.git(我喜欢“sharedGameState”而不是“sharedInstance”)
希望这会帮助其他人尝试做同样的事情......这是我工作的 NSCoder GameState 单例:
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
保存:
- (void)applicationWillTerminate:(UIApplication *)application
...
[GameState saveState];
...
加载中:
// somewhere in your app, maybe in applicationDidFinishLaunching
GameState *state = [GameState sharedGameState];
NSLog(@"sharedGameState: %@", state);
[GameState loadState];
如果有人对此有任何疑问,请说出来。 :)
不过,它似乎工作正常。
【讨论】:
【参考方案2】:您无需下载修改后的宏。原来的 allocWithZone 返回 nil。只需像这样更正原始的:
来自:
+ (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; \
\
【讨论】:
以上是关于游戏状态单例 cocos2d,initWithEncoder 总是返回 null的主要内容,如果未能解决你的问题,请参考以下文章