SKPhysicsContact 崩溃的扩展

extension for SKPhysicsContact crashing

我正在使用 SpriteKit 创建一个游戏,该游戏在 2 个物体之间发生碰撞。设置主体后,我实现了 didBegin(_contact:) 方法,如下所示:

func didBegin(_ contact: SKPhysicsContact) {
    if contact.bodyA.categoryBitMask == 0 && contact.bodyB.categoryBitMask == 1 {
        gameOver()
    }
}

而且效果很好。

后来,在查看此方法的文档时,我发现了以下内容:

The two physics bodies described in the contact parameter are not passed in a guaranteed order.

所以为了安全起见,我扩展了 SKPhysicsContact class 一个函数,可以在两个主体之间交换 categoryBitMask,如下所示:

extension SKPhysicsContact {

    func bodiesAreFromCategories(_ a: UInt32, and b: UInt32) -> Bool {
        if self.bodyA.categoryBitMask == a && self.bodyB.categoryBitMask == b { return true }
        if self.bodyA.categoryBitMask == b && self.bodyB.categoryBitMask == a { return true }
        return false
    }
}

问题是当函数被调用时,应用程序崩溃,我收到以下错误:

2017-07-18 13:44:18.548 iSnake Retro[17606:735367] -[PKPhysicsContact bodiesAreFromCategories:and:]: unrecognized selector sent to instance 0x60000028b950

2017-07-18 13:44:18.563 iSnake Retro[17606:735367] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PKPhysicsContact bodiesAreFromCategories:and:]: unrecognized selector sent to instance 0x60000028b950'

SKPhysicsContact 是 class 到 PKPhysicsContact 的包装器,你正在扩展 SKPhysicsContact 但实际上你需要扩展 PKPhysicsContact (你可以'做)

要保持​​联系方式的顺序,只需执行以下操作:

let bodyA = contact.bodyA.categoryBitMask <= self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB

let bodyB = contact.bodyA.categoryBitMask > self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB

这样当你需要检查一个特定的节点时,你知道要命中哪个节点,所以

func didBegin(_ contact: SKPhysicsContact) {
    if contact.bodyA.categoryBitMask == 0 && contact.bodyB.categoryBitMask == 1 {
        gameOver()
    }
}

变成

func didBegin(_ contact: SKPhysicsContact) {

    let bodyA = contact.bodyA.categoryBitMask <= self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB

    let bodyB = contact.bodyA.categoryBitMask > self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
    if bodyA.categoryBitMask == 0 && bodyB.categoryBitMask == 1 {
        gameOver()
    }
}

然后您可以添加到您的代码中,因为您现在知道了各个主体。

func didBegin(_ contact: SKPhysicsContact) {

    let bodyA = contact.bodyA.categoryBitMask <= self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB

    let bodyB = contact.bodyA.categoryBitMask > self.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
    if bodyA.categoryBitMask == 0 && bodyB.categoryBitMask == 1 {
        gameOver()
        //since I know bodyB is 1,  let's add an emitter effect on bodyB.node
    }
}

顺便说一句,对于看到此答案的人来说,categoryBitMask 0 不应触发任何联系人,您需要在其中提供某种价值才能工作。这是一个超出作者问题范围的错误,所以我将其保留在 0 和 1 处,因为那是 his/her 代码正在做的事情,并且他声称它有效。

这显然是一个错误,如此处所回答:

The problem is, the type of contact is PKPhysicsContact (as you've noticed), even when you explicitly tell it to be an SKPhysicsContact, and the extension is on SKPhysicsContact. You'd have to be able to make an extension to PKPhysicsContact for this to work. From this logic, we can say that no instance methods will work in SKPhysicsContact extensions at the moment. I'd say it's a bug with SpriteKit, and you should file a radar. Class methods still work since you call them on the class itself.

In the meantime, you should be able to move that method into your scene or another object and call it there successfully.

For the record, this is not a Swift-specific problem. If you make the same method in an Objective-C category on SKPhysicsContact you'll get the same crash.

您可以向苹果提交错误报告:

https://developer.apple.com/bug-reporting/

并向社区报告:

https://openradar.appspot.com/search?query=spritekit

但是,您真正想用代码做的是将类别掩码加在一起。然后检查总和(2 + 4 和 4 + 2 总是等于 6,无论 bodyA 和 bodyB 顺序如何)。

这就是您获得 唯一联系方式 的方式,前提是您以 2 的幂(2、4、8、16 等)正确设置掩码