Swift: executing commands upon contact between nodes

58 views Asked by At

Right now, I have a brick that falls down the screen. When it hits my square nothing happens. As you can see in the image below, I have four triangles that make up a square in the same spot, but with a low opacity. This is to help differentiate between what side of the square is contacted. However, nothing respawns or despawns win the brick hits the triange (the trianges that make up the square.)

Image of Game

    func spawnBrick()  {
            let randomFunc = [self.spawnbrickTop, self.spawnbrickBottom, self.spawnbrickLeft, self.spawnbrickRight]
            let randomResult = Int(arc4random_uniform(UInt32(randomFunc.count)))
            randomFunc[randomResult]()
        }



    func spawnbrickTop() {

        brickTop.size = CGSize(width: 210, height: 105)
        brickTop.name = "BrickTop"
        brickTop.position = CGPoint(x: frame.midX, y: frame.maxY)
        brickTop.zPosition = 1.5
        //physics stuff begins here
        brickTop.physicsBody = SKPhysicsBody(circleOfRadius: max(brickTop.size.width / 2,
                                                                 brickTop.size.height / 2))
        brickTop.physicsBody?.categoryBitMask = PhysicsCategories.brickTopCategory
        brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
        PhysicsCategories.basicBottomCategory |
        PhysicsCategories.basicLeftCategory |
        PhysicsCategories.basicRightCategory
        brickTop.physicsBody?.collisionBitMask = PhysicsCategories.none
         //bye bye physics
        self.addChild(brickTop)

    }

    func spawnbrickBottom() {

        brickBottom.size = CGSize(width: 230, height: 101)
        brickBottom.name = "BrickBottom"
        brickBottom.position = CGPoint(x: frame.midX, y: frame.maxY)
        brickBottom.zPosition = 1.5
        //physics stuff begins here
        brickBottom.physicsBody = SKPhysicsBody(circleOfRadius: max(brickBottom.size.width / 2,
                                                                    brickBottom.size.height / 2))
        brickBottom.physicsBody?.categoryBitMask = PhysicsCategories.brickBottomCategory
        brickBottom.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
        PhysicsCategories.basicBottomCategory |
        PhysicsCategories.basicLeftCategory |
        PhysicsCategories.basicRightCategory
        brickBottom.physicsBody?.collisionBitMask = PhysicsCategories.none
         //bye bye physics
        self.addChild(brickBottom)
    }

    func spawnbrickLeft() {


        brickLeft.size = CGSize(width: 170, height: 80)
        brickLeft.name = "BrickLeft"
        brickLeft.position = CGPoint(x: frame.midX, y: frame.maxY)
        brickLeft.zPosition = 1.5
        //physics stuff begins here
        brickLeft.physicsBody = SKPhysicsBody(circleOfRadius: max(brickLeft.size.width / 2,
                                                                  brickLeft.size.height / 2))
        brickLeft.physicsBody?.categoryBitMask = PhysicsCategories.brickLeftCategory
        brickLeft.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
        PhysicsCategories.basicBottomCategory |
        PhysicsCategories.basicLeftCategory |
        PhysicsCategories.basicRightCategory
        brickLeft.physicsBody?.collisionBitMask = PhysicsCategories.none
         //bye bye physics
        self.addChild(brickLeft)

    }


    func spawnbrickRight() {

        brickRight.size = CGSize(width: 140, height: 95)
        brickRight.name = "BrickRight"
        brickRight.position = CGPoint(x: frame.midX, y: frame.maxY)
        brickRight.zPosition = 1.5
        //physics stuff begins here
        brickRight.physicsBody = SKPhysicsBody(circleOfRadius: max(brickRight.size.width / 2,
                                          brickRight.size.height / 2))
        brickRight.physicsBody?.categoryBitMask = PhysicsCategories.brickRightCategory
        brickRight.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
        PhysicsCategories.basicBottomCategory |
        PhysicsCategories.basicLeftCategory |
        PhysicsCategories.basicRightCategory
        brickRight.physicsBody?.collisionBitMask = PhysicsCategories.none
         //bye bye physics
        self.addChild(brickRight)

    }


    func spawnBasicBrick() {
            basicBrick.size = CGSize(width: 200, height: 177.6)
            basicBrick.position = CGPoint(x: frame.midX, y: frame.minY + basicBrick.size.width)
            basicBrick.zPosition = 1


            basicBrick.physicsBody = SKPhysicsBody(rectangleOf: basicBrick.size)
            basicBrick.physicsBody?.categoryBitMask = PhysicsCategories.basicBrickCategory
            basicBrick.physicsBody?.isDynamic = false
            basicBrick.physicsBody?.allowsRotation = true

            addChild(basicBrick)
        }
        func spawnBasicTop() {

            basicTop.size = CGSize(width: 400, height: 400)
            basicTop.position = CGPoint(x: 230, y: 200)
            basicTop.zPosition = 1.5
            basicTop.alpha = 0.3
            basicTop.name = "BasicTop"

            //physics stuff begins here
            basicTop.physicsBody = SKPhysicsBody(rectangleOf: basicTop.size)
            basicTop.physicsBody?.categoryBitMask = PhysicsCategories.basicTopCategory
            basicTop.physicsBody?.isDynamic = false
            basicTop.physicsBody?.allowsRotation = true
            //bye bye physics


            addChild(basicTop)
        }

        func spawnBasicBottom() {

            basicBottom.size = CGSize(width: 400, height: 400)
            basicBottom.position = CGPoint(x: 230, y: 200)
            basicBottom.zPosition = 1.5
            basicBottom.alpha = 0.3
            basicBottom.name = "BasicBottom"

            //physics stuff begins here
            basicBottom.physicsBody = SKPhysicsBody(rectangleOf: basicBottom.size)
            basicBottom.physicsBody?.categoryBitMask = PhysicsCategories.basicBottomCategory
            basicBottom.physicsBody?.isDynamic = false
            //bye bye physics


            addChild(basicBottom)
        }
        func spawnBasicLeft() {

            basicLeft.size = CGSize(width: 400, height: 400)
            basicLeft.position = CGPoint(x: 230, y: 200)
            basicLeft.zPosition = 1.5
            basicLeft.alpha = 0.3
            basicLeft.name = "BasicLeft"

            //physics stuff begins here
            basicLeft.physicsBody = SKPhysicsBody(rectangleOf: basicLeft.size)
            basicLeft.physicsBody?.categoryBitMask = PhysicsCategories.basicLeftCategory
            basicLeft.physicsBody?.isDynamic = false
            //bye bye physics


            addChild(basicLeft)
        }

        func spawnBasicRight() {

            basicRight.size = CGSize(width: 400, height: 400)
            basicRight.position = CGPoint(x: 230, y: 200)
            basicRight.zPosition = 1.5
            basicRight.alpha = 0.3
            basicRight.name = "BasicRight"

            //physics stuff begins here
            basicRight.physicsBody = SKPhysicsBody(rectangleOf: basicRight.size)
            basicRight.physicsBody?.categoryBitMask = PhysicsCategories.basicRightCategory
            basicRight.physicsBody?.isDynamic = false
            //bye bye physics


            addChild(basicRight)
        }



extension GameScene: SKPhysicsContactDelegate {

    func didBegin(_ contact: SKPhysicsContact) {
        //01
        //10
        //11
        let contactMask = contact.bodyA.categoryBitMask |
            contact.bodyB.categoryBitMask

        if contactMask == PhysicsCategories.brickTopCategory |
            (PhysicsCategories.basicTopCategory) {
            if let brickTop = contact.bodyA.node?.name == "BrickTop" ? contact.bodyA.node
                as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
            if contact.bodyA.node?.name == "BrickTop" &&
               contact.bodyB.node?.name == "BasicTop" {

                    print("Correct!")
                    brickTop.run(SKAction.fadeOut(withDuration: 0.05), completion: {
                        self.brickTop.removeFromParent()
                        self.spawnBrick()
                    })
            }
    }
           else if contactMask == PhysicsCategories.brickBottomCategory |
                    (PhysicsCategories.basicBottomCategory) {
            if let brickBottom = contact.bodyA.node?.name == "BrickBottom" ? contact.bodyA.node
            as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
                    if contact.bodyA.node?.name == "BrickBottom" &&
                       contact.bodyB.node?.name == "BasicBottom" {

                            print("Correct!")
                            brickBottom.run(SKAction.fadeOut(withDuration: 0.05), completion: {
                                self.brickBottom.removeFromParent()
                                self.spawnBrick()
                            })
                    }
            }
       else if contactMask == PhysicsCategories.brickLeftCategory |
                (PhysicsCategories.basicLeftCategory) {
        if let brickLeft = contact.bodyA.node?.name == "BrickLeft" ? contact.bodyA.node
        as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
                if contact.bodyA.node?.name == "BrickLeft" &&
                   contact.bodyB.node?.name == "BasicLeft" {

                        print("Correct!")
                        brickLeft.run(SKAction.fadeOut(withDuration: 0.05), completion: {
                            self.brickLeft.removeFromParent()
                            self.spawnBrick()
                        })
                }
        }
       else if contactMask == PhysicsCategories.brickRightCategory |
                (PhysicsCategories.basicRightCategory) {
        if let brickRight = contact.bodyA.node?.name == "BrickRight" ? contact.bodyA.node
        as? SKSpriteNode : contact.bodyB.node as? SKSpriteNode {
                if contact.bodyA.node?.name == "BrickRight" &&
                   contact.bodyB.node?.name == "BasicRight" {

                        print("Correct!")
                        brickRight.run(SKAction.fadeOut(withDuration: 0.05), completion: {
                            self.brickRight.removeFromParent()
                            self.spawnBrick()
                        })
                }
        }
        else {
            gameOver()
        }
  }
 }
}
}
}
}


enum PhysicsCategories {
    static let none: UInt32 = 0
    static let brickCategory: UInt32 = 1//01
    static let brickTopCategory: UInt32 = 1 //01
    static let brickBottomCategory: UInt32 = 1//01
    static let brickLeftCategory: UInt32 = 1//01
    static let brickRightCategory: UInt32 = 1//01

    static let basicTopCategory: UInt32 = 1 //10; shifts all bits to the left
    static let basicBottomCategory: UInt32 = 1 //10; shifts all bits to the left
    static let basicLeftCategory: UInt32 = 1 //10; shifts all bits to the left
    static let basicRightCategory: UInt32 = 1 //10; shifts all bits to the left

    static let basicBrickCategory: UInt32 = 1
}
1

There are 1 answers

0
Lou Franco On

The problem is probably in lines like this:

brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicBottomCategory
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicLeftCategory
brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicRightCategory

In this code, the contact bitmask ends up just being basicRight. You want to combine them. This is assuming that you set up the categories correctly to begin with (as 1, 2, 4, 8, etc)

You want it more like this:


brickTop.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory |
   PhysicsCategories.basicBottomCategory |
   PhysicsCategories.basicLeftCategory |
   PhysicsCategories.basicRightCategory

Here is how this is supposed to work

  1. You have a bunch of nodes in your scene
  2. You group them into categories that have similar behavior
  3. You create a category enum/value for each category as a bit in a mask (1, 2, 4, etc), which is (0x1 << 0, 0x1 << 1, 0x1 << 2)
  4. You put the nodes into categories (using | to combine the category values)
  5. You set the contactTest and collisionDetect bit masks (using | to combine the category values)

I don't know how your game is supposed to work, but there are several things that feel weird about your code.

  1. You use the same number for different categories
  2. You don't combine them into bitmasks.

Imagine I have the game Pong. I want to detect when the ball hits

  1. The sidewalls to bounce it
  2. The top and bottom of the screen to record an out
  3. The paddles to bounce it

Nodes

  1. ball
  2. left wall
  3. right wall
  4. bottom
  5. top
  6. top paddle
  7. bottom paddle
  8. player 1 score
  9. player 2 score

Categories (this is just an example -- it's not the only way)

  1. CategoryNone: 0
  2. CategoryGamePiece: 1
  3. CategoryOutDetector: 2
  4. CategoryNonInteractive: 4 (use for the score nodes)

The ball bounces off the paddles and side walls. It also interacts with invisible out detectors on the top and bottom. It is in two categories (using |)

ball.categoryBitMask = CategoryGamePiece | CategoryOutDetector
ball.collisionBitMask = CategoryGamePiece
ball.contactTestBitMask = CategoryOutDetector

The paddle and side wall only interact with the ball as a game piece (colliding)

paddleTop.categoryBitMask = CategoryGamePiece
paddleTop.collisionBitMask = CategoryGamePiece
paddleTop.contactTestBitMask = CategoryNone

leftWall.categoryBitMask = CategoryGamePiece
leftWall.collisionBitMask = CategoryGamePiece
leftWall.contactTestBitMask = CategoryNone

The bottom only interacts with the ball as a contact test (doesn't change its motion)

bottomOut.categoryBitMask = CategoryOutDetector
bottomOut.collisionBitMask = CategoryNone
bottomOut.contactTestBitMask = CategoryOutDetector