-1
class GameScene: SKScene, SKPhysicsContactDelegate {

    let balls = [
        SKSpriteNode(imageNamed: "blueball.png"),
        SKSpriteNode(imageNamed: "greenball.png"),
        SKSpriteNode(imageNamed: "realredball.png"),
        ]

    let redRectangle = SKSpriteNode(imageNamed: "redrectangle.png")
    let blueRectangle = SKSpriteNode(imageNamed: "bluerectangle.png")
    let greenRectangle = SKSpriteNode(imageNamed: "greenrectangle.png")

    let blueBallCategory: UInt32 = 0x1 << 0
    let greenBallCategory: UInt32 = 0x1 << 1
    let realRedBallCategory: UInt32 = 0x1 << 2
    let redRectangleCategory: UInt32 = 0x1 << 3
    let blueRectangleCategory: UInt32 = 0x1 << 4
    let greenRectangleCategory: UInt32 = 0x1 << 5

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

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        physics()
    }

    func didBegin(_ 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 == blueBallCategory 
          && secondBody.categoryBitMask == redRectangleCategory {
             print("dead")
        }
    }

    func spawnBallsandRectangles() {
        let ball = balls[Int(arc4random_uniform(UInt32(balls.count)))]
        ball.position = CGPoint(x: 0, y: 250)
        ball.size = CGSize(width: 70, height: 70)

        balls[0].physicsBody?.categoryBitMask = blueBallCategory
        balls[1].physicsBody?.categoryBitMask = greenBallCategory
        balls[2].physicsBody?.categoryBitMask = realRedBallCategory
        balls[0].physicsBody?.contactTestBitMask = redRectangleCategory

        redRectangle.position = CGPoint(x: 0, y: -600)
        redRectangle.size = CGSize(width: 200, height: 20)
        redRectangle.physicsBody?.categoryBitMask = redRectangleCategory

        blueRectangle.position = CGPoint(x: -200, y: -600)
        blueRectangle.size = CGSize(width: 200, height: 20)
        blueRectangle.physicsBody?.categoryBitMask = blueRectangleCategory

        greenRectangle.position = CGPoint(x: 200, y: -600)
        greenRectangle.size = CGSize(width: 200, height: 20)
        greenRectangle.physicsBody?.categoryBitMask = greenRectangleCategory

        self.addChild(ball)
        self.addChild(redRectangle)
        self.addChild(blueRectangle)
        self.addChild(greenRectangle)
    }

    func physics() {
        for ball in balls {
            ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height/2)
        }

        redRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
        redRectangle.physicsBody?.affectedByGravity = false
        redRectangle.physicsBody?.isDynamic = false

        blueRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
        blueRectangle.physicsBody?.affectedByGravity = false

        greenRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
        greenRectangle.physicsBody?.affectedByGravity = false

    }
}

I want the blue ball(ball[0]) to hit the redRectangle node and trigger a collision detection. I programmed it so the console would print ("dead") if a collision was detected when the blue ball landed on the red rectangle. It doesn't do that.

Note: Either a blue, red or green ball falls from the top of the screen and lands on a blue, red or green rectangle. But so far i have only set up the detect collision code for the blue ball and red rectangle. I am hoping someone can help, thanks a lot.

Lësha Turkowski
  • 1,361
  • 1
  • 10
  • 21

1 Answers1

0

The issue is that you are assigning a SKPhysicsBody to the nodes in your method physics(). The method spawnBallsandRectangles() assigns values to the categoryBitMask property of those bodies. Since you call spawnBallsandRectangles() before physics() the bodies don't exist yet and the assignment of the bit masks fails.

I would move all the setup code from physics() to spawnBallsandRectangles() so that you can setup the physics bodies before attempting to assign values to their properties. To allow the balls to stay still until touched you can either pause the scene with self.isPaused = true and then start it again later with self.isPaused = false or use the isDynamic property on the balls so they are not dynamic until touched.

You can use this technique to detect which ball is touched and then make it dynamic once it has been touched: How do I detect if an SKSpriteNode has been touched

I re-wrote your code using this last technique to demonstrate.

class GameScene: SKScene, SKPhysicsContactDelegate {
  let balls = [
    SKSpriteNode(imageNamed: "blueball.png"),
    SKSpriteNode(imageNamed: "greenball.png"),
    SKSpriteNode(imageNamed: "realredball.png"),
    ]

  let redRectangle = SKSpriteNode(imageNamed: "redrectangle.png")
  let blueRectangle = SKSpriteNode(imageNamed: "bluerectangle.png")
  let greenRectangle = SKSpriteNode(imageNamed: "greenrectangle.png")

  let blueBallCategory: UInt32 = 0x1 << 0
  let greenBallCategory: UInt32 = 0x1 << 1
  let realRedBallCategory: UInt32 = 0x1 << 2
  let redRectangleCategory: UInt32 = 0x1 << 3
  let blueRectangleCategory: UInt32 = 0x1 << 4
  let greenRectangleCategory: UInt32 = 0x1 << 5

  override func didMove(to view: SKView) {
    // set up all properties for balls and rectangles
    spawnBallsandRectangles()
    self.physicsWorld.contactDelegate = self
  }

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    // find node touched
    guard let touched = touches.first else { return }
    let position = touched.location(in: self)
    let touchedNode = self.atPoint(position)

    // if it's a ball then start it falling
    if touchedNode.name == "ball" {
      touchedNode.physicsBody?.isDynamic = true
    }
  }

  func didBegin(_ 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 == blueBallCategory
      && secondBody.categoryBitMask == redRectangleCategory {
      print("dead")
    }
  }

  /// Convenience method to set up the rectangles
  static func setupRectangle(rectangle: SKSpriteNode,
                             category: UInt32,
                             position: CGPoint) {
    rectangle.position = position
    rectangle.size = CGSize(width: 200, height: 20)
    let physicsBody = SKPhysicsBody(rectangleOf: rectangle.size)
    physicsBody.affectedByGravity = false
    physicsBody.isDynamic = false
    physicsBody.categoryBitMask = category
    rectangle.physicsBody = physicsBody
  }

  func spawnBallsandRectangles() {
    for ball in balls {
      // name them to later identify them when touched
      ball.name = "ball"

      // give it a physics body and freeze it
      let physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height/2)
      physicsBody.isDynamic = false
      ball.physicsBody = physicsBody
    }

    let ball = balls[Int(arc4random_uniform(UInt32(balls.count)))]
    ball.position = CGPoint(x: 0, y: 250)
    ball.size = CGSize(width: 70, height: 70)

    balls[0].physicsBody?.categoryBitMask = blueBallCategory
    balls[1].physicsBody?.categoryBitMask = greenBallCategory
    balls[2].physicsBody?.categoryBitMask = realRedBallCategory
    balls[0].physicsBody?.contactTestBitMask = redRectangleCategory

    GameScene.setupRectangle(rectangle: redRectangle,
                             category: redRectangleCategory,
                             position: CGPoint(x: 0, y: -600))
    GameScene.setupRectangle(rectangle: blueRectangle,
                             category: blueRectangleCategory,
                             position: CGPoint(x: -200, y: -600))
    GameScene.setupRectangle(rectangle: greenRectangle,
                             category: greenRectangleCategory,
                             position: CGPoint(x: 200, y: -600))

    self.addChild(ball)
    self.addChild(redRectangle)
    self.addChild(blueRectangle)
    self.addChild(greenRectangle)
  }
}