3

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

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

它工作得很好。

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

接触参数中描述的两个物理体不是以保证的顺序传递的

所以为了安全起见,我SKPhysicsContact用一个函数扩展了这个类,在两个主体之间交换 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 bodyAreFromCategories:and:]:无法识别的选择器发送到实例 0x60000028b950

2017-07-18 13:44:18.563 iSnake Retro[17606:735367] *** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[PKPhysicsContact bodyAreFromCategories:and:]:无法识别的选择器发送到实例 0x60000028b950”

4

2 回答 2

2

SKPhysicsContact是一个包装类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,因为这是他/她的代码正在做的事情,并且(s)他声称它有效。

于 2017-07-18T11:57:12.040 回答
2

这显然是一个错误,如此处回答: https ://stackoverflow.com/a/33423409/6593818

问题是,联系人的类型是 PKPhysicsContact(如您所见),即使您明确告诉它是 SKPhysicsContact,并且扩展名在 SKPhysicsContact 上。您必须能够对 PKPhysicsContact 进行扩展才能使其正常工作。根据这个逻辑,我们可以说目前没有实例方法可以在 SKPhysicsContact 扩展中工作。我会说这是 SpriteKit 的一个错误,你应该提交一个雷达。类方法仍然有效,因为您在类本身上调用它们。

同时,您应该能够将该方法移动到您的场景或其他对象中并成功调用它。

郑重声明,这不是 Swift 特有的问题。如果您在 SKPhysicsContact 上的 Objective-C 类别中使用相同的方法,您将遇到相同的崩溃。

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

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 等)正确设置面罩,这就是您获得独特联系人的方式

于 2017-07-18T12:03:58.143 回答