1

背景信息: 我正在创建一个具有多个“楼层”的 SpriteKit 2D 平台式游戏。当用户进入“门户”时,他被传送到另一层(上一层或下一层)。如果用户死在与最近出生点不同的楼层,他会被传送回出生点所在的楼层。

问题:升级到 iOS8 后,这种情况会导致游戏崩溃并出现EXC_BAD_ACCESS异常/错误。即使在阅读了有关如何调试此类错误的多个教程之后,我似乎也找不到问题所在。

因此,如果有人可以查看我的代码并告诉我他们是否发现了更好的方法来做我正在做的事情,我将非常感激,最好不会导致游戏冻结。

游戏布局: 我有一个自定义类TileMapLayer,它基于 Ray Wenderlich 的书iOS Games By Tutorials中的同一类。它基本上是一个自定义的 SKNode 类,包含多个 32x32p 瓦片,结合为我的游戏创建背景。

我在游戏开始时加载适当的楼层,然后在我的用户上/下时调用以加载另一个楼层。在这种情况下,我首先将当前楼层缓存在 a 中NSDictionary,以便稍后在用户返回该楼层时访问它。

// Cache the current floor
[_allFloors setObject:_bgLayer forKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)_currentFloor]];

// Remove the current floor
[_bgLayer removeFromParent];

// Get the cached (next) floor if it exists, if not create it
TileMapLayer *cachedFloor = [_allFloors objectForKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)(_currentFloor + 1)]];
if (!cachedFloor) {
    _bgLayer = [self createScenery:[NSNumber numberWithInteger:(_currentFloor + 1)]];
    NSLog(@"creating new floor");
} else {
    _bgLayer = cachedFloor;
    NSLog(@"getting cached floor");
}

// Display the new floor
[_worldNode addChild:_bgLayer];

// Increment the floor number
_currentFloor++;

(我也有类似的下楼方法)。这在升级到 iOS8 之前和之后都非常有效。

当用户死亡时,他会被传送回最后一个出生点。如果最后一个生成点位于不同的楼层,则楼层也会相应更改。我将球本身的这个自定义 SKAction 称为动画的一部分:

SKAction *changeFloor = [SKAction runBlock:^{
  if (self.spawnFloor != _currentFloor) { 
    [_bgLayer removeFromParent];
    _bgLayer = [_allFloors objectForKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)self.spawnFloor]];
    [_worldNode addChild:_bgLayer];
    _currentFloor = self.spawnFloor;
    NSLog(@"Made it here");
  }
}];

如您所见,没有太大区别。被Made it here记录到控制台,但游戏立即冻结。我调用的下一个方法(将球移动到正确的位置)根本没有执行。

只是为了好玩,我尝试_bgLayer在将其从父级中删除之前对其进行缓存,如下所示:

if (self.spawnFloor != _currentFloor) {
  [_allFloors setObject:_bgLayer forKey:[NSString stringWithFormat:@"floor_%tu", (unsigned)_currentFloor]];
  ...

出于某种原因,当我这样做时,游戏并没有冻结。然而,地板最终混合在一起,就好像_bgLayer从未从其父级中移除一样(注意:“旧”瓷砖不再对任何物理模拟做出反应,它们只是在后台什么都不做)。

从父级删除后,我也尝试过[_bgLayer removeAllChildren](删除单个图块):

[_bgLayer removeFromParent];
[_bgLayer removeAllChildren];

但这会导致当我重生后返回此楼层时 _bgLayer 为空。好像我在将其存储在字典中之前删除了所有节点。我的猜测是字典只引用 _bgLayer 的位置,而不是内容本身。因此,当我删除屏幕上的所有子项时,我也有效地删除了缓存中的所有子项。

总结: 我知道这是一个很长的问题,如果你能做到这一点,我想说声谢谢。如果您有任何问题,请随时在下面的评论中提问。最终,我想知道的是:我怎样才能解决我的问题,这样游戏才不会死机?在不引起内存问题的情况下缓存地板的最佳做法是什么?在屏幕上删除和添加节点的最佳实践是什么,并且总是将当前楼层(屏幕上的那个)称为_bgLayer

再次,谢谢你!

4

1 回答 1

2

iOS8 在删除块内的节点时似乎表现不同,就好像您尝试在后台线程上执行此操作一样,因此有时会导致奇怪的崩溃。这可能是一个错误,但在那之前我会建议两件事:

1.- 在块内标记要删除的节点,但在update:循环中执行。您不会注意到任何差异。

2.- 确保 removeFromParent 发生在主线程中。但不确定这会解决问题。

__strong typeof(_bgLayer) strongLayer = _bgLayer;
dispatch_async(dispatch_get_main_queue(), ^{
        [strongLayer removeFromParent];
    });
于 2014-10-08T13:34:58.797 回答