简体   繁体   中英

SpriteKit Collision Detection Not Working Properly

I have three nodes in my game-

The Player -

This is a boat that you drag around using a joystick.

let collisionPlayer : UInt32 = 0x1 << 1

let apple = SKSpriteNode(texture: texture)
    apple.position = position
    apple.physicsBody = SKPhysicsBody(circleOfRadius: apple.size.width / 2.0)
    apple.physicsBody?.affectedByGravity = false
    apple.physicsBody?.isDynamic = true
    apple.setScale(0.1)
    addChild(apple)

("apple" is the boat)

The Enemy Boat -

This is a CPU boat that randomly follows the player around.

 let collisionNPC : UInt32 = 0x1 << 0


 appleNPC.position = position
    appleNPC.physicsBody = SKPhysicsBody(circleOfRadius: appleNPC.size.width / 2.0)
    appleNPC.physicsBody?.isDynamic = true
    appleNPC.physicsBody?.affectedByGravity = false
    appleNPC.position = CGPoint(x: 600, y: 200)

    appleNPC.setScale(0.1)
    addChild(appleNPC)

The Bullet (self-explanatory)

let collisionBullet : UInt32 = 0x1 << 2

(The following code is in TouchesEnded)

  bullet?.name = "Bullet"
    bullet?.position = (appleNode?.position)!
    bullet?.setScale(0.05)
    bullet?.zPosition = 1
    bullet?.physicsBody = SKPhysicsBody(circleOfRadius: (bullet?.size.width)!/2)
    bullet?.physicsBody?.isDynamic = true
    bullet?.physicsBody?.affectedByGravity = false
    bullet?.physicsBody?.usesPreciseCollisionDetection = true

To move the bullet I use the code:

    // Your code with delay
    if bulletNumbers > 0 {
        let offset = CGPoint(x: touchLocation.x - (bullet?.position.x)! , y: touchLocation.y - (bullet?.position.y)!)

        //Stops Bullet from shooting backwards


        //Get the direction of where to shoot
        let direction = offset

        //Make it shoot far enough to be guaranteed off screen
        let shootAmount = CGPoint(x: direction.x * 10, y: direction.y * 10)

        //Add the shoot amount to the current position
        let realDest = CGPoint(x: shootAmount.x + (bullet?.position.x)!, y: shootAmount.y + (bullet?.position.y)!)

        //Create the actions
        addChild(bullet!)
        self.bulletNumbers -= 1

        let distance = sqrt(pow(realDest.x - (appleNode?.position.x)!, 2) +
            pow(realDest.y - (appleNode?.position.y)!, 2))

        // run the sequence of actions for the firing
        let duration = TimeInterval(distance / PlayerMissileSpeed)
        let missileMoveAction = SKAction.move(to: realDest, duration: duration)
        let when = DispatchTime.now() + 10 // change 2 to desired number of seconds
        DispatchQueue.main.asyncAfter(deadline: when) { self.bulletNumbers += 1
        }
        bullet?.run(missileMoveAction) {
        self.bullet?.isHidden = true
        self.bullet?.removeFromParent()
        print("\(self.bulletNumbers)")
        }}
    else if bulletNumbers <= 0 {
      print("reload")

        let when = DispatchTime.now() + 2 // change 2 to desired number of seconds
        DispatchQueue.main.asyncAfter(deadline: when) {

        }

    }


}

func didBegin(_ contact: SKPhysicsContact) {
    print("test")


}

func setRandomStickColor() {
    let randomColor = UIColor.random()
    moveAnalogStick.stick.color = randomColor
}

func setRandomSubstrateColor() {
    let randomColor = UIColor.random()
    moveAnalogStick.substrate.color = randomColor
}

override func update(_ currentTime: TimeInterval) {
    /* Called before each frame is rendered */
    super.update(currentTime)
    let _:UInt32 = arc4random_uniform(1) //

    appleNodeCpuSpeed = CGFloat(1)


    var locationx : CGFloat
    var locationy: CGFloat

    locationx = (appleNode?.position.x)!
    locationy = (appleNode?.position.y)!
    let dx = locationx - (appleNodeNPC?.position.x)!
    let dy = locationy - (appleNodeNPC?.position.y)!
    let angle = atan2(dy, dx)
    let vx = cos(angle) * appleNodeCpuSpeed
    let vy = sin(angle) * appleNodeCpuSpeed


    self.appleNodeNPC?.position.x += vx
    self.appleNodeNPC?.position.y += vy

    let when = DispatchTime.now() + 0.25 // change 2 to desired number of seconds
    DispatchQueue.main.asyncAfter(deadline: when) {

          self.appleNodeNPC?.zRotation = angle + 90

    }
    //2

This works fine. The bullet is launched from the boat to the touch location, however, things start to go wrong when I tried to introduce collisions. Here is my code below:

 appleNode?.physicsBody?.categoryBitMask = collisionPlayer
    appleNode?.physicsBody?.collisionBitMask = collisionNPC
    appleNode?.physicsBody?.contactTestBitMask = 0

    appleNodeNPC?.physicsBody?.categoryBitMask = collisionNPC
    appleNodeNPC?.physicsBody?.collisionBitMask = collisionPlayer
    appleNodeNPC?.physicsBody?.contactTestBitMask = collisionBullet

    bullet?.physicsBody?.categoryBitMask = collisionBullet
    bullet?.physicsBody?.collisionBitMask = 0
    bullet?.physicsBody?.contactTestBitMask = collisionNPC


    physicsWorld.contactDelegate = self
    view.showsPhysics = true

Then in my didBegin function I have:

func didBegin(_ contact: SKPhysicsContact) {
    print("test")


}

Let's start from the beginning. I tap the screen and the bullet is launched. The bullet does not collide with the Player, which is what I originally wanted. However, when the bullet makes contact with the Enemy ship it DOES collide. It goes around the ship and keeps going on to its original destination. I want the bullet to contact the ship and nothing else - no collisions. I would hope to assume that my error here is because of my code for moving the bullet, but i'm not sure. Any help is appreciated.

  1. Define unique categories, ensure your class is a SKPhysicsContactDelegate and make yourself the physics contact delegate:

//Physics categories

let appleCategory:   UInt32 = 1 << 0
let enemyCategory:    UInt32 = 1 << 1
let bulletCategory:   UInt32 = 1 << 2

class GameScene: SKScene, SKPhysicsContactDelegate {
   physicsWorld.contactDelegate = self
  1. Assign the categories (usually in didMove(to view:) :

    apple.physicsBody.catgeoryBitMask = appleCategory enemy.physicsBody.catgeoryBitMask = enemyCategory bullet.physicsBody.catgeoryBitMask = bulletCategory

(Make sure you've created physics bodies for each node)

  1. Set up collisions:

apple.physicsBody?.collisionBitMask = 0 // apple/player collides with nothing enemy.physicsBody?.collisionBitMask = 0 // enemy collides with nothing bullet.physicsBody?.collisionBitMask = 0 // bullet collides with nothing

or even:

for node in [apple, enemy, bullet] {
   node.physicsBody?.collisionBitMask = 0 //  collides with nothing
   }
  1. Set up contacts

    bullet.physicsBody?.collisionBitMask = enemyCategory // bullet contacts enemy

Make sure that at least one of the objects involved in each potential contact has the isDynamic property on its physics body set to true , or no contact will be generated. It is not necessary for both of the objects to be dynamic.

You should now get didBegin called when the bullet and the enemy make contact. You could code didBegin like this:

  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 bulletCategory | enemyCategory:
        print("bullet and enemy have contacted.")
        let bulletNode = contact.bodyA.categoryBitMask == bulletCategory ? contact.bodyA.node : contact.bodyB.node
        enemyHealth -= 10
        bulletNode.removeFromParent
     default:
        print("Some other contact occurred")
     }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM