Trouble with Collision Detection

I am making a game right now in SpriteKit, and the purpose of the game is that there is a gunner who shoots bullets, and the goal is to shoot the balls. Right now, I have added basic collision detection so that when a bullet hits a ball, the ball will be moved to the right from the impact of the bullet. I want to make it so when the bullet touches the ball, the ball explodes. I know that I will need a .atlas file for the animation of the bullet, but how can I make it so the game recognizes when the bullet touches the ball, then sets off the animation.

Hi ExpertExp,


Implement your GameScene as the SKPhysicsContactDelegate


Add your categoryBitMasks and contactTestBitMasks to your ball and bullet physics bodies

ball.physicsBody!.categoryBitMask = Category.Ball.rawValue
ball.physicsBody!.contactTestBitMask = Category.Bullet.rawValue


bullet.physicsBody!.categoryBitMask = Category.Bullet.rawValue
bullet.physicsBody!.contactTestBitMask =  Category.Ball.raw


Implement didBeginContact: in your GameScene

func didBeginContact(contact: SKPhysicsContact) {
        let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
        switch contactMask {
        case Category.Ball.rawValue | Category.Bullet.rawValue:
            if let ball = contact.bodyA.node as? Ball, bullet = contact.bodyB.node as? Bullet {
               // Your blow up the ball code
                ball.blowUp()
                bullet.removeFromParent()
                playLoudExplosionSoundFX()
  
            } else if bullet = contact.bodyA.node as? Bullet, ball = contact.bodyB.node as? Ball {
                ball.blowUp()
                bullet.removeFromParent()
                playLoudExplosionSoundFX()
            }
        default: break
        }
}


There is probably a cleaner way to implement your blow up the ball code in didBeginContact:, without repeating code.


I hope this helps


Jim

Thank you for your help Jim. It was what I was looking for: however, when I implemented it into my code, I recieved a couple of errors. I debugged some of them, but most of them were past my level.


Can someone please look over this code and tell me where I have made mistakes.


Thank you,

Patrick


This is my code -



import UIKit

import SpriteKit

class ShooterScene: SKScene {


var score = 0

var enemyCount = 50

var shooterAnimation = [SKTexture]()


override func didMoveToView(view: SKView) {

/Changes Gravity

self.physicsWorld.gravity = CGVectorMake(0, -1)

*/

self.initShooterScene()

}




func initShooterScene(){

let shooterAtlas = SKTextureAtlas(named: "shooter")

for index in 1...shooterAtlas.textureNames.count {

let imgName = String(format: "shooter%01d", index)

shooterAnimation += [shooterAtlas.textureNamed(imgName)]

}

let dropBalls = SKAction.sequence([SKAction.runBlock({

self.createBallNode()}),

SKAction.waitForDuration(0.25)])

self.runAction(SKAction.repeatAction(dropBalls, count: enemyCount), completion: nil)

}


override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {

let shooterNode = self.childNodeWithName("shooterNode")

if(shooterNode != nil) {

let animation = SKAction.animateWithTextures(shooterAnimation, timePerFrame: 0.075)

let shootBullet = SKAction.runBlock({

let BulletNode = self.createBulletNode()

self.addChild(BulletNode)

BulletNode.physicsBody?.applyImpulse(CGVectorMake(300, 0))

})

let sequence = SKAction.sequence([animation, shootBullet])

shooterNode?.runAction(sequence)

}

}


func createBulletNode() -> SKSpriteNode {

let shooterNode = self.childNodeWithName("shooterNode")

let shooterPosition = shooterNode?.position

let shooterWidth = shooterNode?.frame.size.width

let Bullet = SKSpriteNode(imageNamed: "bullet.png")

Bullet.position = CGPointMake(shooterPosition!.x + shooterWidth!/2, shooterPosition!.y + 60)

Bullet.name = "BulletNode"

Bullet.physicsBody = SKPhysicsBody(rectangleOfSize: Bullet.frame.size)

Bullet.physicsBody?.usesPreciseCollisionDetection = true

Bullet.physicsBody!.categoryBitMask = Category.Bullet.rawValue ! Error - 'Category.Type' does not have a member named 'Bullet'

Bullet.physicsBody!.contactTestBitMask = Category.Ball.rawValue ! Error - 'Category.Type' does not have a member named 'Ball'

return Bullet

}


func createBallNode() {

let Ball = SKSpriteNode(imageNamed: "starball.png")

Ball.position = CGPointMake(randomNumber(self.size.width) + 250, self.size.height)

Ball.name = "BallNode"

Ball.physicsBody = SKPhysicsBody(circleOfRadius: Ball.size.width/2)

Ball.physicsBody?.usesPreciseCollisionDetection = true

Ball.physicsBody!.categoryBitMask = Category.Ball.rawValue ! Error - 'Category.Type' does not have a member named 'Ball' Ball.physicsBody!.contactTestBitMask = Category.Bullet.rawValue ! Error - 'Category.Type' does not have a member named 'Bullet'

self.addChild(Ball)

}


func randomNumber(maximum: CGFloat) -> CGFloat {

let maxInt = UInt32(maximum)

let result = arc4random_uniform(maxInt)

return CGFloat(result)

}

func didBeginContact(contact: SKPhysicsContact) {

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

switch contactMask {

case Category.Ball.rawValue | Category.Bullet.rawValue: !Error - 'Category.Type' does not have a member named 'Bullet' if let Ball = contact.bodyA.node as? Ball, Bullet = contact.bodyB.node as? Bullet { !Error - Use of undeclared variable 'Ball'

/

Ball.blowUp()

Bullet.removeFromParent()

playLoudExplosionSoundFX()

} else if Bullet = contact.bodyA.node as? Bullet && Ball = contact.bodyB.node as? Ball {

Ball.blowUp()

Bullet.removeFromParent()

playLoudExplosionSoundFX()

}

default: break

}

}




}

Hi Patrick,


Did you implement your Categories as an Enumerator first?


At the top of your GameScene, above your class decleration. Add a Category Enumerator


enum Category: UInt32 {
    case Ball = 1
    case Bullet = 2
    case AnotherEntity = 4 // additional entities are added by doubling the previous value.
}


These are the values of the categoryBitMask.


The way your Contact delegate calculates (sort of), or, the way I think of it is this...


Your Ball has a value of 1 and your Bullet has a value of 2...


Set your Bullet to have the category value of

categoryBitMask = Category.Bullet.rawValue

categoryBitMask = 2


Set your Bullet to test for contact

contactTestBitMask = Category.Ball.rawValue

contactTestBitMask = 1


your PhysicsContactDelegate will register the contacts as integers. So your switch statement is

case: Category.Bullet.rawValue | Category.Ball.rawValue

2 + 1 = 3


When your Contact delegate registers a contact of 3 your Bullet and Ball have contacted each other.

If you add one of your categories to the collisionBitMask, the entities will collide.


I hope this helps.


Cheers


Jim

Thank you Jim for helping me. I really appreciate the work you put in to help me. Your code was very helpful and I learned a lot from it. Thank you very much.


-Patrick

Trouble with Collision Detection
 
 
Q