为什么 didBegin 没有关于接触体的数据?

why didBegin has no data about contact bodies?

我正在制作一个玩家(球)和敌人(球)互相撞击的游戏。有时 didBegin func 保存有关两个碰撞体的信息,有时不保存,这会导致崩溃并抛出 exc_bad_instruction 错误,然后我用断点检查玩家和敌人并找到它们 nil.这是怎么发生的,有时它会起作用,有时却不起作用。请帮助。

我的代码:

private var player: Player!

private func drawPlayer(color: SKColor, radius: CGFloat, cn: String) {
        player = Player.factory.createPlayer(color: color, radius: radius)
        player.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
        player.name = cn
        world.addChild(player)
    }

if (contact.bodyA.categoryBitMask == bodyType.enemy.rawValue) && (contact.bodyB.categoryBitMask == bodyType.player.rawValue) {
        print(contact.bodyA.node?.name)
        print(contact.bodyB.node?.name)
        let player = contact.bodyB.node as? Player
        let hitter = contact.bodyA.node as? Enemy
}

我用印刷品来获取名字,有时它会给我两个名字,有时它会给我两个 nil 。其余代码我没有放,因为这里不需要。

解决方案::

史蒂夫在他的回答中提到的问题是,有时 didBegin 方法会在相同的碰撞对象上被调用两次。所以它崩溃了,因为在第一次调用时我删除了对象所以它第二次找到 nil 。所以正如史蒂夫提到的那样,你应该用 if 包围逻辑并检查两个主体是否是 nil 然后这是 didBegin 的第二次调用并且对象已经被删除所以你不应该运行 逻辑,否则它将 运行 什么都没有然后崩溃。如果它们不是 nil 你可以 运行 代码并且它有效。

如果 bodyA 是玩家,bodyB 是敌人会怎样?我怀疑这种情况是您 "and sometimes no, which lead to a crash" 问题的原因。

didBegin() 中,您是否从场景中删除了任何节点(使用 removeFromParent)?这可能会导致行为,因为 SK 有时会为一次碰撞生成对 didBegin 的多次调用。如果您在第一次调用 didBegin 时删除了碰撞中涉及的一个或两个节点,那么在第二次调用它时(这是在同一个游戏循环中)您的一些节点 and/or 他们的属性可以是 nil

请记住,在 SKMPhysicsContact 对象中传递给 didBegin 的对象是物理实体,而不是节点本身。即使您已经删除了节点本身(在之前的 didBegin 中),物理实体也可能仍然存在。这意味着除非您访问 didBegin 中的实际节点,否则您可能不会崩溃,并且如果您正在进行一些简单的检查,例如如果玩家击中了一个硬币或一个敌人,那么你可能会增加两倍的分数,或者移除两批生命等。

有很多方法可以解决这个问题 - 包括但不限于:

  • 在对它做任何事情之前检查它是否为零。 (如果它是零,那么假设你已经处理了它的碰撞并删除了它)。
  • 将要删除的节点添加到集合中,然后删除所有节点 在 didFinishUpdate 的集合中,在对 didBegin 的所有调用之后 已作出。您可能需要在节点 userData 属性 中设置一个标志以阻止它被处理两次。

再加上一些其他技巧。搜索 sprite-kit 多次碰撞。

为了处理 'which is bodyA and which is bodyB' 问题,我喜欢这样编写 didBegin: 代码:

    func didBeginContact(contact: SKPhysicsContact) {
        let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

        switch contactMask {

        case categoryBitMask.player | categoryBitMask.enemy:
           print("Collision between player and enemy")
           let enemy = contact.bodyA.categoryBitMask == categoryBitMask.thisMine ? contact.bodyA.node! : contact.bodyB.node!
           enemy.explode()

        default :
           //Some other contact has occurred
           print("Some other contact")
    }  
}

只有当您的节点一次只属于一个类别时,这才是真正安全的,具体由您决定。