Ben Vanderloos Ben Vanderloos - 7 months ago 39
Swift Question

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") }

}


}

Answer

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

Comments