Search code examples
xcodeswiftsprite-kitcollisiondetection

Trouble with collision detection swift 2.0 spritekit


I am building a game for iOS for the first time. I am very close, however I got collision detection working early then I changed something and I lost it working. I have tried to get in back but haven't work. This is what I have so far, but nothing working. I am trying different versions of collision detection that I have found online but I am sticking to get this one to work. Just don't understand where I have went wrong.

Thank you in advanced.

import SpriteKit
enum ColliderType: UInt32 {
    case Player = 1
    case Traffic = 2
}


class GameScene: SKScene, SKPhysicsContactDelegate {
    override func didMoveToView(view: SKView) {

createWalls()

    self.backgroundColor = SKColor.whiteColor()
    self.physicsWorld.gravity = CGVectorMake(0, 0)

    player.physicsBody = SKPhysicsBody(rectangleOfSize: player.size)
    player.physicsBody?.dynamic = true

    player.physicsBody!.categoryBitMask = ColliderType.Player.rawValue
    player.physicsBody!.contactTestBitMask = ColliderType.Traffic.rawValue

    player.zPosition = 3
    player.name = "player"
    player.position = CGPoint(x: self.frame.midX, y: (self.frame.midY)/3)
    player.setScale(1.0)

    self.addChild(player)

    _ = NSTimer.scheduledTimerWithTimeInterval(2.5, target: self, selector: #selector(GameScene.makeTraffic), userInfo: nil, repeats: true)
}

func makeTraffic(){

    var aNumber = Int(arc4random_uniform(2))

    let Pos1 = Int(self.frame.midX/2)+30
    let Pos2 = Int((self.frame.size.width)/2)
    let Pos3 = Int(self.frame.size.width-300)

    let array = [Pos1, Pos2, Pos3]
    let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
    let randomPOS = CGPoint(x:Int(array[randomIndex]), y:Int(self.frame.height))

    if aNumber == 0 {
        aNumber = aNumber + 1
    }

    switch aNumber {
    case 1:
        let car1 = SKSpriteNode(imageNamed: "Car_Green_Front")
        car1.position = randomPOS
        car1.zPosition = 3
        car1.setScale(1.0)

        car1.physicsBody = SKPhysicsBody(rectangleOfSize: car1.size)
        car1.physicsBody?.dynamic = true
        car1.physicsBody!.categoryBitMask = ColliderType.Player.rawValue
        car1.physicsBody!.contactTestBitMask = ColliderType.Traffic.rawValue

        self.addChild(car1)
    case 2:
        let car2 = SKSpriteNode(imageNamed: "Car_Purple_Front")
        car2.position = randomPOS
        car2.zPosition = 3
        car2.setScale(1.0)

        car2.physicsBody = SKPhysicsBody(rectangleOfSize: car2.size)
        car2.physicsBody?.dynamic = true
        car2.physicsBody!.categoryBitMask = ColliderType.Traffic.rawValue
        car2.physicsBody!.contactTestBitMask = ColliderType.Player.rawValue

        self.addChild(car2)
    default:
        return
    }
}

func createWalls(){
    let wallSize = CGSize(width: 5, height: self.frame.size.height)

    let rightwall = SKShapeNode(rectOfSize: wallSize)
    rightwall.physicsBody = SKPhysicsBody(rectangleOfSize: wallSize)
    rightwall.physicsBody!.dynamic = false

    rightwall.position = CGPoint(x: self.frame.maxX-300, y: self.frame.size.height/2)
    rightwall.fillColor = UIColor.clearColor()
    self.addChild(rightwall)

    let leftwall = SKShapeNode(rectOfSize: wallSize)
    leftwall.physicsBody = SKPhysicsBody(rectangleOfSize: wallSize)
    leftwall.physicsBody!.dynamic = false

    leftwall.position = CGPoint(x: self.frame.minX+300, y: self.frame.size.height/2)
    leftwall.fillColor = UIColor.clearColor()
    self.addChild(leftwall)
}

func didBeginContact(contact: SKPhysicsContact) {
print("Contact")

if contact.bodyA.categoryBitMask == ColliderType.Traffic.rawValue && contact.bodyB.categoryBitMask == ColliderType.Player.rawValue {

print("Hi")

} else {

print("Hello")    }

}

}


Solution

  • Your code seems alrite, although there is some small changes I would make.

    1) I would write my collider types like so

     struct ColliderType {
         static let player: UInt32 = 0x1 << 0
         static let traffic: UInt32 = 0x1 << 1
     }
    

    because this way you only need to increment the last number by 1. Your way if you decide to add more categories the next one would have to be 4, than 8, than 16 etc, which is more confusing (you are dealing with 32 bit integers)

    Than use it like so

     ...categoryBitMask = ColliderType.player
    

    2) It is recommended that you give the sprite the position before adding the physicsBody. You are doing it the other way round which could cause unexpected issues.

    3) Change your collision method to this

    func didBeginContact(contact: SKPhysicsContact) {
        var firstBody: SKPhysicsBody
        var secondBody: SKPhysicsBody
    
        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            firstBody = contact.bodyA
            secondBody = contact.bodyB
        } else {
            firstBody = contact.bodyB
            secondBody = contact.bodyA
        }
    
        if (firstBody.categoryBitMask == ColliderType.player) && (secondBody.categoryBitMask == ColliderType.traffic) {
            // player hit traffic, do something 
        }
     }
    

    4) Finally the most important part is that you need to set the delegate which I couldn't see in your code.

    Call this in DidMoveToView

     physicsWorld.contactDelegate = self
    

    otherwise the DidBeginContact method will never fire.

    Also it is a good idea if you follow apples naming conventions. So only classes, protocols, enums and structs should start with capital letters.

    Hope this helps