2014-10-11 83 views
1

我正在开发一款可产生大块2d瓷砖的iOS游戏,用户可以“无限”地移动。将NSCoding对象写入磁盘时性能下降

仅适用于上下文:每个图块都是32x32px,每个“块”包含4x4图块(共16个)。大多数瓷砖都是空的,但是当某些东西被放置在瓷砖上时,并且如果用户移动太远并且该块“卸载” - 我将块和瓷砖数据保存到磁盘。

现在,大块和小块都使用NSCoding协议。块保存瓷砖和一些布尔值的NSArray,并且每个瓷砖节省几个数字/布尔值。

这个效果非常好 - 当大块卸载时它会保存,当块再次被加载时,它会被恢复。

但是,我注意到写入磁盘方法极其简单地杀死了我的FPS。由于随着玩家的移动,通常会有几个块被卸载在一起,当他们被卸载时,玩家已经进一步移动并卸载 - 这加起来了。

我已经尽我所能确保只保存实际更改的块,并且我最好组织保存代码以避免每次保存调用都实例化对象。

有没有更好的方式可以保存数据而不影响FPS?

  • 因为我通常保存屏幕上不可见的数据,我可以做这个异步吗?
  • 是否有比NSCoding更快的方法,因为它只是每个类的一些float/bool值?

这里的组块保存代码:

-(void)saveToDisk { 
    if(!self.hasUnsavedChanges){ 
     NSLog(@"skipping saving chunk that has not changed"); 
     return; 
    } 
    [self.saveFile writeData:self withKey:self.savefileKey]; 
    self.hasUnsavedChanges = false; 
} 

-(void)encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeObject:[NSNumber numberWithFloat:self.position.x] forKey:@"chunk.x"]; 
    [encoder encodeObject:[NSNumber numberWithFloat:self.position.y] forKey:@"chunk.y"]; 
    [encoder encodeObject:tiles forKey:@"chunk.tiles"]; 
} 

-(id)initWithCoder:(NSCoder *)decoder { 

    Chunk *restore = [self init]; 

    NSNumber *xChunkNum = [decoder decodeObjectForKey:@"chunk.x"]; 
    NSNumber *yChunkNum = [decoder decodeObjectForKey:@"chunk.y"]; 
    restore.position = [Coord coordWithX:[xChunkNum intValue] Y:[yChunkNum intValue]]; 

    tiles = [decoder decodeObjectForKey:@"chunk.tiles"]; 

    return restore; 
} 

而且瓷砖代码:

-(void)encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeObject:[NSNumber numberWithFloat:self.worldContextCoordinate.x] forKey:@"tile.world.x"]; 
    [encoder encodeObject:[NSNumber numberWithFloat:self.worldContextCoordinate.y] forKey:@"tile.world.y"]; 
    [encoder encodeObject:[NSNumber numberWithFloat:self.color.red] forKey:@"tile.color.red"]; 
    [encoder encodeObject:[NSNumber numberWithFloat:self.color.green] forKey:@"tile.color.green"]; 
    [encoder encodeObject:[NSNumber numberWithFloat:self.color.blue] forKey:@"tile.color.blue"]; 
    [encoder encodeObject:[NSNumber numberWithFloat:self.isSourceTile] forKey:@"tile.isSource"];forKey:@"tile.hasBlackNeighbor"]; 
} 

-(id)initWithCoder:(NSCoder *)decoder { 

    Tile *restore = [self init]; 

    NSNumber *xNum = [decoder decodeObjectForKey:@"tile.world.x"]; 
    NSNumber *yNum = [decoder decodeObjectForKey:@"tile.world.y"]; 
    [restore setWorldContextCoordinate:b2Vec2([xNum floatValue], [yNum floatValue])]; 

    NSNumber *red = [decoder decodeObjectForKey:@"tile.color.red"]; 
    NSNumber *green = [decoder decodeObjectForKey:@"tile.color.green"]; 
    NSNumber *blue = [decoder decodeObjectForKey:@"tile.color.blue"]; 

    restore.color = [CCColor colorWithRed:[red floatValue] green:[green floatValue] blue:[blue floatValue]]; 

    restore.isSourceTile = [decoder decodeObjectForKey:@"tile.isSource"]; 

    return restore; 
} 
+0

您是否完全保存这些?如在其中,是否可能发生了一件事情,要求在它开始之前将它们保存到磁盘中?如果是这样,那么异步可能会成为一个噩梦,即使完成块。除了CoreData之外,你可能在NSCoding的正确轨道上。 – brandonscript 2014-10-11 16:36:13

+0

不是。这些通常在我“卸载”它们时保存 - 当用户离开时不再需要这个“土地”。所有需要最终结果的是数据再次加载时,当用户回到这个位置并加载块数据时。 – helion3 2014-10-11 16:50:44

+0

使用分析器和/或时间记录来确定时间。然后你可以决定加速什么。你会知道NSCoding花费的时间和磁盘写入时间。 – zaph 2014-10-11 17:11:37

回答

0

鉴于这一事实,你并不需要担心影响UI或UX完成事件,你应该能够异步运行它们。如果需要也可以使用完成块来完成此操作,以便在必要时处理完成事件。

- (void)saveToDiskAsyncWithCompletion:(void(^)(BOOL finished))completion 
{  
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NO), ^{ 
     [self saveToDisk]; 
     // or you could run any of your other operations here too. 
     dispatch_async(dispatch_get_main_queue(), ^(void){ 
      completion(YES); 
     }); 
    }); 
} 
+0

你认为有什么可能的方式来加载数据异步吗?查看新的性能分析数据和我的Tile对象的initWithCoder仍然是一个相当大的时间,仍然影响fps。 – helion3 2014-10-11 23:16:36

+0

有 - 您需要使用类似于您的方法的完成块,以便在系统完成时通知系统。调用方法时,将UI更新放入完成块中。 – brandonscript 2014-10-11 23:18:34