游戏状态单例cocos2d,initWithEncoder总是返回null

发布于 2024-08-29 03:27:15 字数 3687 浏览 12 评论 0原文

我试图在 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 中,但这似乎也不起作用。

再次提前致谢。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

潦草背影 2024-09-05 03:27:16

正义!我想我明白了。另外,我发现了一个很好的省时宏来启动: 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

Saving:< /strong>


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

正在加载:


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

如果有人发现任何问题,请大声说出来。 :)

不过,它似乎工作得很好。

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.

智商已欠费 2024-09-05 03:27:16

您不需要下载修改后的宏。原来的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; \
} \

You don't need to download the modified macro. The original allocWithZone returned nil. Just correct the original one like this:

from:

+ (id)allocWithZone:(NSZone *)zone \
{ \
    @synchronized(self) \
    { \
        if (shared##classname == nil) \
        { \
            shared##classname = [super allocWithZone:zone]; \
            return shared##classname; \
        } \
    } \
    \
    return nil; \
} \

to:

+ (id)allocWithZone:(NSZone *)zone \
{ \
    @synchronized(self) \
    { \
        if (shared##classname == nil) \
        { \
            shared##classname = [super allocWithZone:zone]; \
        } \
    } \
    \
    return shared##classname; \
} \
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文