Andrew Andrew - 4 days ago 4
Swift Question

Nodes are colliding but not responding to didBeginContact function

My nodes are colliding when i run it. The coin bounces of the player node. However, when I want to call the didBeginContact function it is not responding...
I want eventually a label to display the score +1 every time the coin hits the player. Also the coin should disappear when colliding with the player. But my contact is not working so I can't make any collision rules make the label display the score.

import SpriteKit
import GameplayKit

// Collision categories

struct physicsCategory {
static let playerCat : UInt32 = 1
static let coinCat : UInt32 = 2
}

class GameScene: SKScene, controls, SKPhysicsContactDelegate {

let player = SKSpriteNode(imageNamed:"trump")
let points = SKLabelNode()
let buttonDirLeft = SKSpriteNode(imageNamed: "left")
let buttonDirRight = SKSpriteNode(imageNamed: "right")
let background = SKSpriteNode(imageNamed: "background")
var pressedButtons = [SKSpriteNode]()
let popUpMenu = SKSpriteNode(imageNamed: "popupmenu")
var score = 0
var gameOver = false
var startGame = false
var rules = false

override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self

//score label
points.position = CGPoint(x: 530, y: 260)
points.text = ("\(score)")
points.zPosition = 6
points.fontColor = UIColor.black
points.fontSize = 50
addChild(points)

//Set Background
background.zPosition = 1
background.position = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
background.size.width = 580
background.size.height = 320

addChild(background)

// Player
player.position = CGPoint(x: 250, y: 40)
player.zPosition = 2
player.size.width = 40
player.size.height = 60
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
player.physicsBody?.affectedByGravity = false
player.physicsBody!.categoryBitMask = physicsCategory.playerCat
player.physicsBody!.contactTestBitMask = physicsCategory.coinCat
player.physicsBody?.collisionBitMask = 0
player.physicsBody?.isDynamic = false
self.addChild(player)

//contact has started
func didBeginContact(contact: SKPhysicsContact){
let firstBody: SKPhysicsBody = contact.bodyA
let secondBody: SKPhysicsBody = contact.bodyB

if ((firstBody.categoryBitMask == physicsCategory.playerCat) && (secondBody.categoryBitMask == physicsCategory.coinCat)){
CollisionWithCoin(player: firstBody.node as! SKSpriteNode, coins: secondBody.node as! SKSpriteNode)
}
}

func CollisionWithCoin(player: SKSpriteNode, coins:SKSpriteNode){
NSLog("Hello")
}


//repeat coing spawning
run(SKAction.repeatForever(
SKAction.sequence([
SKAction.run(spawnCoins),
SKAction.wait(forDuration: 1.0)])))
}
//coin settings
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}

func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}

//spawn coins
func spawnCoins() {
// 2
let coins = SKSpriteNode(imageNamed: "coins")
coins.zPosition = 2
coins.size.width = 25
coins.size.height = 25
coins.physicsBody = SKPhysicsBody(rectangleOf: coins.size )
coins.physicsBody!.categoryBitMask = physicsCategory.coinCat
coins.physicsBody!.contactTestBitMask = physicsCategory.playerCat
coins.physicsBody?.collisionBitMask = 1
coins.position = CGPoint(x: frame.size.width * random(min: 0, max: 1), y: frame.size.height + coins.size.height/2)

let action = SKAction.moveTo(y: -350, duration: TimeInterval(random(min: 1, max: 5)))

let remove = SKAction.run({coins.removeFromParent()})

let sequence = SKAction.sequence([action,remove])


coins.run(sequence)

addChild(coins)

}

override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
/* Called before each frame is rendered */

if pressedButtons.index(of: buttonDirLeft) != nil {
player.position.x -= 4.0
}
if pressedButtons.index(of: buttonDirRight) != nil {
player.position.x += 4.0
}

}
//MOVEMENT FUNCTIONS START HERE
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

for touch: AnyObject in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)

for button in [buttonDirLeft, buttonDirRight] {
// I check if they are already registered in the list
if button.contains(location) && pressedButtons.index(of: button) == nil {
pressedButtons.append(button)

}
}
}
}


override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)

for button in [buttonDirLeft, buttonDirRight] {
// if I get off the button where my finger was before
if button.contains(previousLocation)
&& !button.contains(location) {
// I remove it from the list
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}

// if I get on the button where I wasn't previously
else if !button.contains(previousLocation)
&& button.contains(location)
&& pressedButtons.index(of: button) == nil {
// I add it to the list
pressedButtons.append(button)

}}}}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)

for button in [buttonDirLeft, buttonDirRight] {
if button.contains(location) {
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
else if (button.contains(previousLocation)) {
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
}
}
}


override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)

for button in [buttonDirLeft, buttonDirRight] {
if button.contains(location) {
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
else if (button.contains(previousLocation)) {
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
}
}
}

}

Answer

Can you try defining your bit masks like this.

  enum PhysicsCategory {
      static let playerCat: UInt32 =  0x1 << 0
      static let coinCat: UInt32 =  0x1 << 1
}

and can you try this code in your contact method. Also note that if you are using Swift 3 the name of the contact method name has changed.

  //contact has started
  func didBegin(_ contact: SKPhysicsContact) {
        let firstBody: SKPhysicsBody = contact.bodyA
        let secondBody: SKPhysicsBody = contact.bodyB


        if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
            firstBody = contact.bodyA
            secondBody = contact.bodyB
        } else {
            firstBody = contact.bodyB
            secondBody = contact.bodyA
        }

        if ((firstBody.categoryBitMask == physicsCategory.playerCat) &&    (secondBody.categoryBitMask == physicsCategory.coinCat)){
            CollisionWithCoin(player: firstBody.node as! SKSpriteNode, coins: secondBody.node as! SKSpriteNode)
    }
   }
}

You are also using a few ! in your code which makes it less safe. Try using ? and "if let" whenever possible when dealing with optionals. So for example write your physics bodies like this even though you know you just created it. You are doing it sometimes and other times you are using !, be consistent.

  player.physicsBody?.categoryBitMask...
  etc

If that physics body for some reason is/becomes nil and you are using ! you will crash.

I would also write your contact method like this, to ensure you also dont crash if the contact methods fires more than once for the same collision.

  func collisionWithCoin(player: SKSpriteNode?, coins:SKSpriteNode?){
         guard let player = player, let coins = coins else { return }

         print("Hello")
  } 

and than call it like so in the didBeginContact method

 collisionWithCoin(player: firstBody.node as? SKSpriteNode, coins: secondBody.node as? SKSpriteNode)

Finally I would also try to follow the swift guidelines, your methods should start with small letters and classes, structs should start with capital letters.

Hope this helps

Comments