奇怪的SpriteKit For Loop Bug

时间:2015-07-03 05:45:49

标签: ios swift sprite-kit

我遇到了一个我无法弄清楚的非常奇怪的错误。在我的游戏中,你驾驶一辆2D飞机上的汽车试图避开障碍物(如岩石或汽车)。障碍可以出现在3条不同的路径上。有时在任何路径上只有一个障碍物,或者有时在三条路径中的两条路径上有两个障碍物。

我遇到的问题是,当两个障碍物出现然后当它们离开屏幕时被删除时,屏幕上当前移动的障碍之一也被“删除”(我知道这是因为控制台打印“已删除的对象“ 3次)。但是,随机“删除”的障碍不会从视图中删除。相反,它冻结了之前的状态,永远不会消失。

以下是我遇到问题的代码块:

var canGetScore = true
for (num,obj) in enumerate(obstaclesToMove) {
    obj.position.y -= CGFloat(gameSpeed)
    if obj.position.y <= userCar.position.y && obj.name == "NotPassed" {
        obj.name = "Passed"
        if canGetScore {
            canGetScore = false
            score += 1
            scoreLabel.text = "\(score)"
        }
    }
    if obj.position.y <= -CGRectGetMidY(self.frame) {
        obj.removeFromParent()
        obstaclesToMove.removeAtIndex(num)
        println("deleted object")
    }
}

变量键:

  • “barriersToMove”是一系列障碍物(SKSpriteNodes),应该在屏幕上移动。

  • “gameSpeed”是屏幕每帧移动多少像素的整数(在这种情况下,值为5)。

  • “userCar”是用户控制的字符(SKSpriteNode)。

“canGetScore”的目的是消除我之前遇到的另一个错误。我知道这不会导致我当前的错误。

有没有人对为什么会这样做有任何想法?如果您需要更多解释,请询问。

额外注意:用户的车在技术上永远不会移动,但背景和障碍物会移动。

2 个答案:

答案 0 :(得分:1)

在我看来,问题在于如何从obstaclesToMove

中删除对象

想象一下,你的数组中有三个对象:

obstaclesToMove = [object1, object2, object3]

你必须删除对象1&amp; 2因为它们必须离开屏幕,所以你在代码中做的是先删除对象1然后删除对象2,除了你的代码转换为:

obstaclesToMove.removeAtIndex(0)
obstaclesToMove.removeAtIndex(1)

你会认为这不是问题,除了看看你的阵列会发生什么:

obstaclesToMove = [object1, object2, object3]
obstaclesToMove.removeAtIndex(0)
// obstaclesToMove = [object2, object3]
obstaclesToMove.removeAtIndex(1)
// obstaclesToMove = [object2]

通常,从正在循环的数组中删除对象是个坏主意。你可以做些什么让它更安全:

  • 收集您需要在数组中删除的所有索引
  • 按相反顺序排序
  • 从数组中删除项目

你可以这样做:

var indexToDelete: [MyObstacleObject] = []
var canGetScore = true
for (num,obj) in enumerate(obstaclesToMove) {
    obj.position.y -= CGFloat(gameSpeed)
    if obj.position.y <= userCar.position.y && obj.name == "NotPassed" {
        obj.name = "Passed"
        if canGetScore {
            canGetScore = false
            score += 1
            scoreLabel.text = "\(score)"
        }
    }
    if obj.position.y <= -CGRectGetMidY(self.frame) {
        indexToDelete.append(num)
    }
}

indexToDelete.sort({ $0 > $1 })

for item in indexToDelete {
    obstaclesToMove[item].removeFromParent()
    obstaclesToMove.removeAtIndex(item)
    println("deleted object")
}

这样,当您从阵列中删除项目时,您可以确保它们仍然具有正确的索引,因为您将首先删除具有更高索引的项目。

答案 1 :(得分:1)

我在游戏中实现了与你的游戏非常相似的东西,所以我猜我所做的一切都可以应用到你的情况中。我使用addObjectremoveObjectIdenticalTo方法(self.obstaclesNSMutableArray)而不是使用索引来消除障碍物中的障碍物:

[self.obstacles addObject:asteroide]; //asteroide is subclass of SKSpriteNode 
[self.obstacles addObject:enemyShip];//enemyShip is subclass of SKSpriteNode 

当我删除它们时,我只是做了类似的事情:

[self.obstacles removeObjectIdenticalTo:asteroide];
[self.obstacles removeObjectIdenticalTo:enemyShip];

这对我有用......我想指出的另一件事是你如何以不同于你的方式移除屏幕外的障碍物。这就是我这样做的方式:

    SKAction *sequence = [SKAction sequence:@[moveAsteroide,[SKAction runBlock:^{[asteroide removeFromParent]; [self.obstacles removeObjectIdenticalTo:asteroide];
               }]]];

  [asteroide runAction:sequence withKey:@"moving"];

所以我运行首先将对象移动到所需位置的序列,这是一个离屏的点(y = -obstacle.size.height),之后,我将其从父级移除并更新障碍阵列。这对我有用,因为我使用动作移动障碍物,但如果你以同样的方式移动障碍物,它可能对你有用。我更喜欢这个,而不是在更新方法中不断检查障碍物的位置。

这只是我的方式,所有这些都可以通过多种方式完成,你必须选择哪一个更适合你。希望这有意义并且有所帮助: - )