添加,删除和缓存SKNodes(Adding, Removing, and Caching SKNo

2019-10-21 02:31发布

背景信息:我创建一个SpriteKit 2D平台式的游戏多“地板”。 当用户进入一个“门户”,他被输送到另一楼层(一方或向上一个向下)。 如果用户去世,享年从那里最近的spawnpoint是不同的楼层,他被运回与spawnpoint地板。

问题:升级到iOS8上后,这种情况会导致游戏的崩溃EXC_BAD_ACCESS异常/错误。 即使通过关于如何调试这样的错误多个教程去后,我似乎无法找出问题所在。

所以,我会很感激,如果有人可以看看我的代码,并告诉我,如果他们看到做我在做什么更好的办法,最好不导致游戏冻结。

游戏布局:我有一个自定义类TileMapLayer这是基于从雷Wenderlich的书的iOS游戏通过教程同一类。 它基本上是一个包含多个32x32p瓷砖定制SKNode类,结合创建我的游戏背景。

我加载相应的地板在游戏开始的时候,然后调用加载另一个楼层时,我的用户进入向上/向下。 在这种情况下,我缓存当前地板首先在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上后。

当用户去世,他被运回最后spawnpoint。 如果最后spawnpoint是在不同的楼层,地板也相应发生变化。 我呼吁球本身作为动画的一部分,这个自定义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

再次,谢谢你!

Answer 1:

iOS8上似乎里面块删除节点时的行为不同,因为如果你想做到这一点在后台线程所以有时候它会导致奇怪的崩溃。 这可能是一个错误,但在那之前我建议两两件事:

1 .-马克节点到块里面删除,但这样做的update:循环。 你不会注意到任何区别。

2:确保removeFromParent发生在主线程。 但不知道这将解决这个问题。

__strong typeof(_bgLayer) strongLayer = _bgLayer;
dispatch_async(dispatch_get_main_queue(), ^{
        [strongLayer removeFromParent];
    });


文章来源: Adding, Removing, and Caching SKNodes