Swift / SpriteKit多重碰撞检测?

时间:2014-11-02 18:43:42

标签: ios swift sprite-kit collision-detection collision

enter image description here

您好。

我遇到了多重碰撞问题。 有一颗子弹击中敌人(红色矩形)。然后,它得分。 有一个螺旋(红色圆圈),当敌人(红色矩形)接触时,它会触发场景结束。

在这种情况下,当敌人击中螺旋时,它会起作用,场景结束,然后我们进入菜单屏幕。 但是,当子弹击中敌人时,同样的事情发生了,我不知道为什么。

现在,这是我的代码:

struct PhysicsCategory {
    static let None : UInt32 = 0
    static let All : UInt32 = UInt32.max
    static let enemyOne : UInt32 = 0b1
    static let enemyTwo : UInt32 = 0b1
    static let bullet : UInt32 = 0b10
    static let spiral : UInt32 = 0b111
}

 spiral.physicsBody = SKPhysicsBody(rectangleOfSize: spiral.size)
        spiral.physicsBody?.categoryBitMask = PhysicsCategory.spiral
        spiral.physicsBody?.contactTestBitMask = PhysicsCategory.enemyOne
        spiral.physicsBody?.collisionBitMask = PhysicsCategory.None
...
        enemyOne.physicsBody = SKPhysicsBody(rectangleOfSize: enemyOne.size)
        enemyOne.physicsBody?.dynamic = true
        enemyOne.physicsBody?.categoryBitMask = PhysicsCategory.enemyOne
        enemyOne.physicsBody?.contactTestBitMask = PhysicsCategory.bullet | PhysicsCategory.spiral
        enemyOne.physicsBody?.collisionBitMask = PhysicsCategory.None

...

        bullet.physicsBody = SKPhysicsBody(circleOfRadius: bullet.size.width / 2)
        bullet.physicsBody?.dynamic = true
        bullet.physicsBody?.categoryBitMask = PhysicsCategory.bullet
        bullet.physicsBody?.contactTestBitMask = PhysicsCategory.enemyOne
        bullet.physicsBody?.collisionBitMask = PhysicsCategory.None
        bullet.physicsBody?.usesPreciseCollisionDetection = true

...

    func bulletDidCollideWithEnemy(bullet: SKSpriteNode, enemyOne: SKSpriteNode) {

        scoreOnScreen.text = String(score)
        score++
        bullet.removeFromParent()
        enemyOne.removeFromParent()
    }

    func enemyDidCollideWithSpiral(enemyOne: SKSpriteNode, spiral: SKSpriteNode) {

        let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
        let skView = self.view! as SKView
        let scene = MenuScene(size: skView.bounds.size)
        scene.scaleMode = SKSceneScaleMode.AspectFill

        skView.presentScene(scene, transition: SKTransition.crossFadeWithDuration(0.5))
    }

 // Did Begin Contact
    func didBeginContact(contact: SKPhysicsContact) {
        var firstBody : SKPhysicsBody
        var secondBody : SKPhysicsBody
        var thirdBody : SKPhysicsBody
        var fourthBody : SKPhysicsBody

        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            firstBody = contact.bodyA
            secondBody = contact.bodyB
        } else {
            firstBody = contact.bodyB
            secondBody = contact.bodyA
        }

        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            thirdBody = contact.bodyA
            fourthBody = contact.bodyB
        } else {
            thirdBody = contact.bodyB
            fourthBody = contact.bodyA
        }



        if (firstBody.categoryBitMask & PhysicsCategory.enemyOne != 0) && (secondBody.categoryBitMask & PhysicsCategory.bullet != 0) {
            bulletDidCollideWithEnemy(firstBody.node as SKSpriteNode, enemyOne : secondBody.node as SKSpriteNode)
        }

        if (thirdBody.categoryBitMask & PhysicsCategory.enemyOne != 0) && (fourthBody.categoryBitMask & PhysicsCategory.spiral != 0) {
            enemyDidCollideWithSpiral(thirdBody.node as SKSpriteNode, spiral : fourthBody.node as SKSpriteNode)
        }

现在,我知道这是一团糟,但任何人都可以帮助我吗?我认为这个问题与bodyA.categoryBitMask和bodyB设置为不同的东西有关,甚至认为它们是相同的(?)。 我不知道。 任何人吗?

1 个答案:

答案 0 :(得分:31)

这里有几个问题。

  1. 您正在以一种不容易测试的方式定义类别。
  2. 您正在以无法获得所需答案的方式测试类别。
  3. 您通过尝试在一个联系人中跟踪最多四个实体来混淆您的代码。任何联系人都会有两个正文。
  4. 让我们一次解决一个......

    1。定义类别

    您希望定义碰撞类别,以便游戏中的每种身体在遮罩中使用自己的位。 (你有一个很好的想法,使用Swift的二进制文字表示法,但你要定义重叠的类别。)这是一个非重叠类别的例子:

    struct PhysicsCategory: OptionSet {
        let rawValue: UInt32
        init(rawValue: UInt32) { self.rawValue = rawValue }
    
        static let enemy  = PhysicsCategory(rawValue: 0b001)
        static let bullet = PhysicsCategory(rawValue: 0b010)
        static let spiral = PhysicsCategory(rawValue: 0b100)
    }
    

    我正在使用Swift OptionSet类型,因为它可以轻松制作和测试唯一值的组合。与enum相比,它确实使定义我的类型及其成员的语法有点笨拙,但这也意味着我以后不需要做很多装箱和拆箱原始值,特别是如果我也做像这样的便利访问者:

    extension SKPhysicsBody {
        var category: PhysicsCategory {
            get {
                return PhysicsCategory(rawValue: self.categoryBitMask)
            }
            set(newValue) {
                self.categoryBitMask = newValue.rawValue
            }
        }
    }
    

    另外,我在我的代码中使用二进制文字符号和额外的空格和零,这样就可以很容易地确保每个类别都有自己的位 - enemy只获得最低位,{{{ 1}}下一个,等等。

    2&amp; 3.测试&amp;跟踪类别

    我喜欢使用双层方法来联系处理程序。首先,我检查一下这种碰撞 - 是子弹/敌人碰撞还是子弹/螺旋碰撞或螺旋/敌人碰撞?然后,如果有必要,我会检查碰撞中的哪个物体是哪个。这在计算方面不会花费太多,并且在我的代码中的每一点都清楚地表明发生了什么。

    bullet

    额外学分

    为什么要使用func didBegin(_ contact: SKPhysicsContact) { // Step 1. To find out what kind of contact we have, // construct a value representing the union of the bodies' categories // (same as the bitwise OR of the raw values) let contactCategory: PhysicsCategory = [contact.bodyA.category, contact.bodyB.category] if contactCategory.contains([.enemy, .bullet]) { // Step 2: We know it's an enemy/bullet contact, so there are only // two possible arrangements for which body is which: if contact.bodyA.category == .enemy { self.handleContact(enemy: contact.bodyA.node!, bullet: contact.bodyB.node!) } else { self.handleContact(enemy: contact.bodyB.node!, bullet: contact.bodyA.node!) } } else if contactCategory.contains([.enemy, .spiral]) { // Here we don't care which body is which, so no need to disambiguate. self.gameOver() } else if contactCategory.contains([.bullet, .spiral]) { print("bullet + spiral contact") // If we don't care about this, we don't necessarily // need to handle it gere. Can either omit this case, // or set up contactTestBitMask so that we // don't even get called for it. } else { // The compiler doesn't know about which possible // contactCategory values we consider valid, so // we need a default case to avoid compile error. // Use this as a debugging aid: preconditionFailure("Unexpected collision type: \(contactCategory)") } } 语句和if类型的OptionSet方法?为什么不做这样的contains()语句,这会使测试值的语法缩短很多?

    switch

    此处使用switch contactCategory { case [.enemy, .bullet]: // ... case [.enemy, .spiral]: // ... // ... default: // ... } 的问题在于它会测试switch相等 - 也就是说,#{1}}时会触发案例#1如果它是OptionSet,那就开火了。

    使用我们在此示例中定义的联系人类别,这不是问题。但是类别/接触位掩码系统的一个很好的功能是,您可以在一个项目上编码多个类别。例如:

    contactCategory == [.enemy, .bullet]

    在这种情况下,您可以拥有一个类别为[.enemy, .bullet, .somethingElse]的联系人 - 如果您的联系处理逻辑专门针对struct PhysicsCategory: OptionSet { // (don't forget rawValue and init) static let ship = PhysicsCategory(rawValue: 0b0001) static let bullet = PhysicsCategory(rawValue: 0b0010) static let spiral = PhysicsCategory(rawValue: 0b0100) static let enemy = PhysicsCategory(rawValue: 0b1000) } friendlyShip.physicsBody!.category = [.ship] enemyShip.physicsBody!.category = [.ship, .enemy] friendlyBullet.physicsBody!.category = [.bullet] enemyBullet.physicsBody!.category = [.bullet, .enemy] 进行测试,您将会错过它。如果您使用[.ship, .bullet, .enemy],则可以测试您关注的特定标志,而无需关心是否存在其他标志。