游戏状态singleton cocos2d,initWithEncoder总是返回null [英] game state singleton cocos2d, initWithEncoder always returns null

查看:113
本文介绍了游戏状态singleton cocos2d,initWithEncoder总是返回null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在cocos2d写一个基本的测试游戏状态单例,但由于某种原因在加载应用程序,initWithCoder不会被调用。



这是我的单身游戏GameState.h:

  
#importcocos2d.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:

  
#importGameState.h
#importConstants.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];
}
//此时sharedState为null,saveFileExists为1
if(sharedState == nil){
//这总是发生
CCLOG 无法加载游戏状态,所以用默认值初始化);
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,@单例的实例);
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

...我正在保存状态app exit,like that:

  
- (void)applicationWillTerminate:(UIApplication *)application {
[GameState saveState];
[[CCDirector sharedDirector] end];
}

...并加载应用程序加载完成后的状态,如下所示:

  
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[GameState loadState];
...
}

loadstate也是,例如在我的主CCScene,但是似乎也不工作。



再次感谢。

解决方案

义人!我想我想出来了。加上我找到一个很好的省时的宏来启动: http: //cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html



和我使用的修改宏:< a href =http://github.com/taberrr/Objective-C-Optimized-Singleton.git =nofollow noreferrer> http://github.com/taberrr/Objective-C-Optimized-Singleton.git (我喜欢sharedGameState超过sharedInstance)



希望这将帮助别人试图做同样的事情...这里是我的工作NSCoder GameState singleton:



GameState.h:

  
#importSynthesizeSingleton.h
#importcocos2d.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:

  
#importSynthesizeSingleton.h
#importGameState.h
#importConstants.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]){
//如果loadState在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){
//不需要将结果设置为任何东西,因为我们只是得到initwithCoder被调用。
//如果你尝试覆盖sharedGameState,会抛出一个断言。
[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协议方法

- (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

/ strong>

  
- (void)applicationWillTerminate:(UIApplication *)application {
...
[GameState saveState];
...
}

  
//在你的应用程序中的某处,也许是在applicationDidFinishLaunching
GameState * state = [GameState sharedGameState] ;
NSLog(@sharedGameState:%@,state);
[GameState loadState];

如果有人发现任何问题,请PLEASE说。 :)



这似乎工作正常。


I'm trying to write a basic test "game state" singleton in cocos2d, but for some reason upon loading the app, initWithCoder is never called. Any help would be much appreciated, thanks.

Here's my 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

... and 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

... I'm saving the state on app exit, like this:


- (void)applicationWillTerminate:(UIApplication *)application {
  [GameState saveState];
  [[CCDirector sharedDirector] end];
}

... and loading the state when the app finishes loading, like this:


- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  ...
  [GameState loadState];
  ...
}

I've tried moving around where I call loadState too, for example in my main CCScene, but that didn't seem to work either.

Thanks again in advance.

解决方案

Righteous! I think I figured it out. Plus I found a nice time-saving macro to boot: http://cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html

And the modified macro I'm using: http://github.com/taberrr/Objective-C-Optimized-Singleton.git (I like "sharedGameState" over "sharedInstance")

Hopefully this will help someone else trying to do the same thing... here's my working 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

Saving:


- (void)applicationWillTerminate:(UIApplication *)application {
  ...
  [GameState saveState];
  ...
}

Loading:


// somewhere in your app, maybe in applicationDidFinishLaunching
GameState *state = [GameState sharedGameState];
NSLog(@"sharedGameState: %@", state);
[GameState loadState];

If someone sees any issues with this, PLEASE speak up. :)

It appears to work fine, though.

这篇关于游戏状态singleton cocos2d,initWithEncoder总是返回null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆