使用 SpriteKit 和 iOS 7 保存高分

Posted

技术标签:

【中文标题】使用 SpriteKit 和 iOS 7 保存高分【英文标题】:Saving High Scores with SpriteKit and iOS 7 【发布时间】:2014-10-05 19:34:25 【问题描述】:

我正在创建一个需要存储多个不同级别的高分的应用。我正在使用http://www.raywenderlich.com/63235/how-to-save-your-game-data-tutorial-part-1-of-2 上的优秀教程作为我正在尝试做的事情的基础。根据他们的建议,我使用了来自 https://gist.github.com/dhoerl/1170641 的 keychainwrapper

在我的 Xcode 外的 ios sim 中,游戏状态保存工作得非常好,我可以关闭 Xcode,甚至重新启动我的 mac,高分就会正常显示。但是当我使用我的 iPhone 5 作为目的地时,它根本不起作用。如果我杀死该应用程序,高分就会消失。不确定我在这笔交易中缺少什么。

这是我的 gameState.h:

#import <Foundation/Foundation.h>

@interface GameState : NSObject <NSCoding>

@property (assign, nonatomic) long score;
@property (assign, nonatomic) long highScoreL1;
@property (assign, nonatomic) long highScoreL2;
@property (nonatomic) long levelIndex;
@property (nonatomic) long lvlIndexMax;

+(instancetype)sharedGameData;
-(void)reset;
-(void)resetAll;
-(void)save;

@end

这是我的 gameState.m

 #import "GameState.h"
 #import "KeychainWrapper.h"

 @implementation GameState

 static NSString* const SSGameDataChecksumKey = @"SSGameDataChecksumKey";
 static NSString* const SSGameDataHighScoreL1Key = @"highScoreL1";
 static NSString* const SSGameDataHighScoreL2Key = @"highScoreL2";

 -(void)encodeWithCoder:(NSCoder *)encoder 
[encoder encodeDouble:self.highScoreL1 forKey:SSGameDataHighScoreL1Key];
[encoder encodeDouble:self.highScoreL2 forKey:SSGameDataHighScoreL2Key];


-(instancetype)initWithCoder:(NSCoder *)decoder 
self = [self init];
if (self) 
    _highScoreL1 = [decoder decodeDoubleForKey:SSGameDataHighScoreL1Key];
    _highScoreL2 = [decoder decodeDoubleForKey:SSGameDataHighScoreL2Key];


return self;


+(NSString*)filePath 
static NSString* filePath = nil;
if (!filePath) 
    filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]stringByAppendingString:@"gamedata"];


return filePath;


+(instancetype)loadInstance 
NSData* decodedData = [NSData dataWithContentsOfFile: [GameState filePath]];
if (decodedData) 
    NSString* checksumOfSavedFile = [KeychainWrapper computeSHA256DigestForData:decodedData];
    NSString* checksumInKeychain = [KeychainWrapper keychainStringFromMatchingIdentifier:SSGameDataChecksumKey];

    if ([checksumOfSavedFile isEqualToString: checksumInKeychain])
        GameState* gameData = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
        return gameData;
    


return [[GameState alloc]init];


-(void)save 
NSData* encodedData = [NSKeyedArchiver archivedDataWithRootObject: self];
[encodedData writeToFile:[GameState filePath] atomically:YES];
NSString* checksum = [KeychainWrapper computeSHA256DigestForData: encodedData];
if ([KeychainWrapper keychainStringFromMatchingIdentifier:SSGameDataChecksumKey]) 
    [KeychainWrapper updateKeychainValue:checksum forIdentifier:SSGameDataChecksumKey];
 else 
    [KeychainWrapper createKeychainValue:checksum forIdentifier:SSGameDataChecksumKey];



+(instancetype)sharedGameData 
static id sharedInstance = nil;

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
    sharedInstance = [self loadInstance];
);

return sharedInstance;


-(void)reset 
self.score = 0;


-(void)resetAll 
self.score = 0;
//self.highScoreL1 = 0;
//self.highScoreL2 = 0;


@end

在每个关卡中我跟踪得分,然后当用户输掉游戏时,我使用以下行来更新高分,然后进入 Game Over 场景:

[GameState sharedGameData].highScoreL1 = MAX([GameState sharedGameData].score, [GameState sharedGameData].highScoreL1);

最后,在 Game Over 中,我使用

[[GameState sharedGameData] save];

保存高分。

提前感谢您的帮助,任何方向都会很棒。如果您需要更多信息,请告诉我!

【问题讨论】:

保存本地分数最安全的方法是使用 nsuserdefaults 例如-(void)setTopscore:(long long)newScore NSString newhighScore = [NSString stringWithFormat:@"%lld", (long long)newScore]; [_userlocalData setObject:newhighScore forKey:topScore]; [_userlocalData 同步]; //从本地数据存储中获取最高分========================================= ============================// -(NSString)getTopscore NSString *newhighScore = [_userlocalData stringForKey:topScore] ;返回新的高分; 【参考方案1】:

在不同场景之间保存本地数据的最安全和最快的方法是使用 nsuserdefaults 例如

//静态变量

static  NSString * topScore= @"topScore";




-(void)setTopscore:(long long)newScore


    NSString *newhighScore = [NSString stringWithFormat:@"%lld", (long long)newScore];

    [_userlocalData setObject:newhighScore forKey:topScore];
    [_userlocalData synchronize];



//get top score from local database storage====================================================================//
-(NSString*)getTopscore

    NSString *newhighScore = [_userlocalData stringForKey:topScore];
    return newhighScore;

我试过了,它在所有 iOS 设备和 mac 本身上都没有任何问题

【讨论】:

明白了。这绝对是有道理的,但我一直读到使用 NSUserDefaults 非常不安全并且超级容易被破解。 另外,iCloud 和 GameCenter 同步将是一个加分项。我想我可以只提取 NSUserDefault 信息并在必要时以这种方式发送。想法? 同意 nsuserdefault 不安全且易于破解,但如果您正在寻找最安全的数据保存方式,请使用 coredata。

以上是关于使用 SpriteKit 和 iOS 7 保存高分的主要内容,如果未能解决你的问题,请参考以下文章

iOS 7 SpriteKit 游戏 - 保存加载游戏时出错:'尝试将 nil 节点添加到父节点:<SKNode> 名称:'(null)'

使用 NSUser Defaults 保存当前游戏分数(不是高分)?

iOS:在本地保存高分

如何使用 Swift 将游戏的高分保存在排行榜上?

如何使用 iOS 7 SpriteKit 粒子向非游戏的 iOS 应用程序添加粒子效果?

SpriteKit iOS 7 Sknode 没有被删除