在SpriteKit中检测多个冲突

时间:2017-11-28 18:42:42

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

我仍然是Swift的初学者,我在SpriteKit中遇到了一些碰撞检测问题。我使用了来自StackOverFlow的this问题,这非常适合展示我如何巧妙地构建事物。但是我的didBegin函数出现了问题,甚至根本没有被调用。我希望我能错过一些简单的东西,你们可以为我看看。

提前致谢。

这里是我的PhysicsCatagoies结构:

import Foundation
import SpriteKit

struct PhysicsCatagories: OptionSet {

    let rawValue: UInt32
    init(rawValue: UInt32) { self.rawValue = rawValue }

    static let None = PhysicsCatagories(rawValue: 0b00000) // Binary for 0
    static let Player = PhysicsCatagories(rawValue: 0b00001) // Binary for 1
    static let EnemyBullet = PhysicsCatagories(rawValue: 0b00010) // Binary for 2
    static let PlayerBullet = PhysicsCatagories(rawValue: 0b00100) // Binary for 4
    static let Enemy = PhysicsCatagories(rawValue: 0b01000) // Binary for 8
    static let Boss = PhysicsCatagories(rawValue: 0b10000) // Binary for 16
}

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

这里是我在 GameScene 中分配节点的方式:

player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
        player.physicsBody!.affectedByGravity = false
        player.physicsBody!.categoryBitMask = PhysicsCatagories.Player.rawValue
        player.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
        player.physicsBody!.category = [.Enemy, .EnemyBullet, .Boss]

bullet.physicsBody = SKPhysicsBody(rectangleOf: bullet.size)
        bullet.physicsBody!.affectedByGravity = false
        bullet.physicsBody!.categoryBitMask = PhysicsCatagories.PlayerBullet.rawValue
        bullet.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
        bullet.physicsBody!.category = [.Enemy, .Boss]

enemy.physicsBody = SKPhysicsBody(rectangleOf: enemy.size)
        enemy.physicsBody!.affectedByGravity = false
        enemy.physicsBody!.categoryBitMask = PhysicsCatagories.Enemy.rawValue
        enemy.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
        enemy.physicsBody!.category = [.Player, .PlayerBullet]

enemyBullet.physicsBody = SKPhysicsBody(rectangleOf: enemyBullet.size)
        enemyBullet.physicsBody!.affectedByGravity = false
        enemyBullet.physicsBody!.categoryBitMask = PhysicsCatagories.EnemyBullet.rawValue
        enemyBullet.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
        enemyBullet.physicsBody!.category = [.Player]

boss.physicsBody = SKPhysicsBody(rectangleOf: boss.size)
        boss.physicsBody!.affectedByGravity = false
        boss.physicsBody!.categoryBitMask = PhysicsCatagories.Boss.rawValue
        boss.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
        boss.physicsBody!.category = [.Player, .PlayerBullet]

bulletSpecial.physicsBody = SKPhysicsBody(rectangleOf: bulletSpecial.size)
            bulletSpecial.physicsBody!.affectedByGravity = false
            bulletSpecial.physicsBody!.categoryBitMask = PhysicsCatagories.PlayerBullet.rawValue
            bulletSpecial.physicsBody!.collisionBitMask = PhysicsCatagories.None.rawValue
            bulletSpecial.physicsBody!.category = [.Enemy, .Boss]

最后,这是我的didBegin函数,它似乎根本不起作用:

func didBegin(_ contact: SKPhysicsContact) {

        let contactCategory: PhysicsCatagories = [contact.bodyA.category, contact.bodyB.category]

        switch contactCategory {

        case [.Player, .Enemy]:
            print("player has hit enemy")
        case [.PlayerBullet, .Enemy]:
            print("player bullet has hit enemy")
        case [.PlayerBullet, .Boss]:
            print("player bullet has hit boss")
        case [.Player, .Boss]:
            print("player has hit boss")
        case [.Player, .EnemyBullet]:
            print("player has hit enemy bullet")
        default:
            preconditionFailure("Unexpected collision type: \(contactCategory)")
        }
    }

1 个答案:

答案 0 :(得分:1)

我没有将OptionSet技术用于cagegoryBitMasks,所以我就是这样做的:

定义唯一类别,确保您的课程为SKPhysicsContactDelegate,并让自己成为物理联系代表:

//Physics categories
let PlayerCategory:         UInt32 = 1 << 0 // b'00001'
let EnemyBulletCategory:    UInt32 = 1 << 1 // b'00010'
let PlayerBulletCategory:   UInt32 = 1 << 2 // b'00100'
let EnemyCategory:          UInt32 = 1 << 3 // b'01000'
let BossCategory:           UInt32 = 1 << 4 // b'10000'

class GameScene: SKScene, SKPhysicsContactDelegate {
   physicsWorld.contactDelegate = self

分配类别(通常在didMove(to view:)

player.physicsBody.catgeoryBitMask = PlayerCategory
bullet.physicsBody.catgeoryBitMask = BulletCategory
enemy.physicsBody.catgeoryBitMask = EnemyCategory
enemyBullet.physicsBody.catgeoryBitMask = EnemyBulletCategory
boss.physicsBody.catgeoryBitMask = BossCategory

(不确定bulletSpecial - 看起来和子弹一样)

设置接触检测:

player.physicsBody?.contactTestBitMask = EnemyCategory | EnemyBulletCategory | BossCategory
bullet.physicsBody?.contactTestBitMask = EnemyCategory | BossCategory
enemy.physicsBody?.contactTestBitMask = PlayerCategory | PlayerBulletCategory
enemyBullet.physicsBody?.contactTestBitMask = PlayerCategory
boss.physicsBody?.contactTestBitMask = PlayerCategory | PlayerBulletCategory

关闭碰撞:(默认情况下已启用)

player.physicsBody?.collisionBitMask = 0
bullet.physicsBody?.collisionBitMask = 0
enemy.physicsBody?.collisionBitMask = 0
enemyBullet.physicsBody?.collisionBitMask = 0
boss.physicsBody?.collisionBitMask = 0

实施didBegin

  func didBegin(_ contact: SKPhysicsContact) {

    print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")

     let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

     switch contactMask {
     case PlayerCategory | EnemyCategory:
        print("player has hit enemy")
     case PlayerBulletCategory | EnemyCategory:
        print("player bullet has hit enemy")
     case PlayerBulletCategory | BossCategory:
        print("player bullet has hit boss")
     case PlayerCategory | BossCategory:
        print("player has hit boss")
     case PlayerCategory | EnemyBulletCategory:
        print("player has hit enemy bullet")
    default:
        print("Undetected collision occurred")
    }
  }

这里有点晚了,所以希望我没有犯过任何愚蠢的错误。

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

您还可以包含此功能,然后在设置完所有物理实体和碰撞并联系位掩码后通过checkPhysics()调用它。它将遍历每个节点并打印出与什么和什么联系人碰撞的内容(它不会检查isDynamic属性,因此请注意):

//MARK: - Analyse the collision/contact set up.
func checkPhysics() {

    // Create an array of all the nodes with physicsBodies
    var physicsNodes = [SKNode]()

    //Get all physics bodies
    enumerateChildNodes(withName: "//.") { node, _ in
        if let _ = node.physicsBody {
            physicsNodes.append(node)
        } else {
            print("\(String(describing: node.name)) does not have a physics body so cannot collide or be involved in contacts.")
        }
    }

    //For each node, check it's category against every other node's collion and contctTest bit mask
    for node in physicsNodes {
        let category = node.physicsBody!.categoryBitMask
        // Identify the node by its category if the name is blank
        let name = node.name != nil ? node.name : "Category \(category)"

        if category == UInt32.max {print("Category for \(String(describing: name)) does not appear to be set correctly as \(category)")}

        let collisionMask = node.physicsBody!.collisionBitMask
        let contactMask = node.physicsBody!.contactTestBitMask

        // If all bits of the collisonmask set, just say it collides with everything.
        if collisionMask == UInt32.max {
            print("\(name) collides with everything")
        }

        for otherNode in physicsNodes {
            if (node != otherNode) && (node.physicsBody?.isDynamic == true) {
                let otherCategory = otherNode.physicsBody!.categoryBitMask
                // Identify the node by its category if the name is blank
                let otherName = otherNode.name != nil ? otherNode.name : "Category \(otherCategory)"

                // If the collisonmask and category match, they will collide
                if ((collisionMask & otherCategory) != 0) && (collisionMask != UInt32.max) {
                    print("\(name) collides with \(String(describing: otherName))")
                }
                // If the contactMAsk and category match, they will contact
                if (contactMask & otherCategory) != 0 {print("\(name) notifies when contacting \(String(describing: otherName))")}
            }
        }
    }
}

它将产生如下输出:

Optional("shape_blueSquare") collides with Optional("Screen_edge")
Optional("shape_redCircle") collides with Optional("Screen_edge")
Optional("shape_redCircle") collides with Optional("shape_blueSquare")
Optional("shape_redCircle") notifies when contacting Optional("shape_purpleSquare")
Optional("shape_redCircle") collides with Optional("shape_greenRect")
Optional("shape_redCircle") notifies when contacting Optional("shape_greenRect")
Optional("shape_purpleSquare") collides with Optional("Screen_edge")
Optional("shape_purpleSquare") collides with Optional("shape_greenRect")
Category for Optional("shape_greenRect") does not appear to be set correctly as 4294967295
Optional("shape_greenRect") collides with Optional("Screen_edge")
Optional("shape_yellowTriangle") notifies when contacting Optional("shape_redCircle")
Optional("shape_yellowTriangle") collides with Optional("shape_greenRect")
Optional("shape_yellowTriangle") notifies when contacting Optional("shape_greenRect")

etc.