Sprite-Kit 为单个接触注册多个碰撞

Ste*_*ves 5 collision-detection sprite-kit

好的 - 我确定这是重复的,而且 Whirlwind 或 KnightOfDragons 已经发布了一个解决方案,但我找不到它。

我正在使用 Sprite-Kit 在 Swift 中编写 Space Invader 的克隆。我遇到的问题是,当入侵者炸弹击中我的船时,代码有时会记录 2 或 3 次碰撞,从而立即结束游戏。这是处理炸弹/船舶碰撞的“didBeginContact”部分:

    case category.bomb.rawValue | category.ship.rawValue:
        print("Bomb hit ship!")
        let bomb = contact.bodyA.categoryBitMask == category.bomb.rawValue ? contact.bodyA.node : contact.bodyB.node
        bomb?.physicsBody?.contactTestBitMask = 0 // Seems to prevent multiple contacts with same bomb
        ship.physicsBody!.contactTestBitMask = 0 // We'll reset this after the death animation
        bomb?.removeAllActions()
        ship.removeAllActions()
        bomb?.removeFromParent()
        ships -= 1

        if ships == 0 { endScene(won: false, withMessage: "A bomb got you!") }
Run Code Online (Sandbox Code Playgroud)

当我运行游戏时,我看到:

Bomb hit ship!
2 ships left.
Run Code Online (Sandbox Code Playgroud)

1颗炸弹命中后(这是正确的)

Bomb hit ship!
1 ships left.
Bomb hit ship!
0 ships left.
Run Code Online (Sandbox Code Playgroud)

在第二次炸弹命中后(这是错误的)。

我从来没有没有注册过联系人,有时(50%?)它工作得很好。其他时候我看到自己有 -4 艘船!我确定这是我出错的明显/基本的东西。

我关于将炸弹和飞船的 contactTestBitMask 设置为 0 的评论显然是错误的。我知道这不应该是必要的,因为我在发生接触时移除了炸弹,所以它不应该再次发生。

如何保证联系人只处理一次?

================================

更新:我添加了 2 条print语句以提供更多调试信息:

        print("bodyA is \(contact.bodyA.node!.name)")
        print("bodyB is \(contact.bodyB.node!.name)")
Run Code Online (Sandbox Code Playgroud)

这是在let bomb = contact.bodyA.category... 声明之后,现在我得到:

Bomb hit ship!
bodyA is Optional("bomb")
bodyB is Optional("playerShip")
1 ships left.
Run Code Online (Sandbox Code Playgroud)

在 1 颗炸弹命中后,并且:

Bomb hit ship!
fatal error: unexpectedly found nil while unwrapping an Optional value
Run Code Online (Sandbox Code Playgroud)

在第二次炸弹命中后。所以在第二次碰撞之后,bodyA 为零,所以我不明白为什么 Sprite-Kit 实际上注册了碰撞?

有任何想法吗?

Ste*_*ves 1

好的 - 看起来很简单:

        if bomb == nil {return}
Run Code Online (Sandbox Code Playgroud)

这就是所需要的。应按如下方式添加:

        let bomb = contact.bodyA.categoryBitMask == category.bomb.rawValue ? contact.bodyA.node : contact.bodyB.node
        if bomb == nil {return}
Run Code Online (Sandbox Code Playgroud)

removeFromParent这可以防止您所在的节点发生多次冲突didBeginContact。如果您不删除节点但仍然注册多个碰撞,则使用节点的userData属性设置某种标志,指示该节点是“anactive”。或者,子类 SKSPriteNode 并添加自定义“isActive”属性,该属性是我做了什么来解决子弹穿过入侵者侧面并消灭入侵者及其上面的入侵者的问题。“直接打击”时绝不会发生这种情况。

它没有回答关于为什么的根本问题注册多个碰撞的根本问题,但这确实意味着我可以删除有关将 contactTestBitMasks 设置为 0 的所有额外代码,然后返回到它们应该稍后设置的值等。

编辑:所以看来,如果您删除 didBeginContact 中的节点,该节点将被删除,但物理体不会被删除。因此,您必须小心,因为 Sprite-Kit 似乎会构建一个已发生的物理接触数组,然后多次调用 dBC,每次接触一次。因此,如果您正在操作节点并在 dBC 中删除它们,请注意,如果您强制解开节点的属性,可能会遇到问题。