0

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
}
  • Don't worry about the sizing of the images in the picture, I have yet to fix it. – Christopher Hahn Feb 25 '20 at 23:16
  • We need to know the values you set the categories to (e.g. what is `PhysicsCategories.brickTopCategory` and all of the others) – Lou Franco Feb 25 '20 at 23:45
  • The values are bitmasks that determine which nodes should be checked for collision. – Lou Franco Feb 25 '20 at 23:46
  • @LouFranco Thanks so much! I am new to programming, so your help is greatly appreciated. I added in the values for those categories in the Settings.swift file. Should I change any of those numbers? – Christopher Hahn Feb 26 '20 at 03:37
  • @Lou Franco After looking over the info in my settings file, do you think I should still change everything to what you said below? – Christopher Hahn Feb 26 '20 at 13:36
  • I added more information to my answer to explain the bitmasks more – Lou Franco Feb 26 '20 at 16:22
  • @Lou Franco I have updated the code to what you suggested. I changed it on this page, too. My game still doesn't execute any code when the brick hits the correct side of the square. I will try and help explain my scenario so your help can be most applicable. In my game, one of four nodes (brick) fall down the screen. Each "brick" has a different shape. At the bottom, there is a square that you can rotate. Since this square is one node, and I need to differentiate between what side of the square it hit, I added an invisible square (opacity turned down) that is comprised of four triangles. – Christopher Hahn Feb 26 '20 at 22:10
  • @LouFranco This allows my to differentiate between what side of the square is hit. Right now, even with the suggestions you gave, nothing happens when my brick hits the triangles. I need it do de-spawn, and for another brick to be spawned at the top. – Christopher Hahn Feb 26 '20 at 22:11
  • @LouFranco Should I add a contactTestBitMask to the basicTop, basicBottom, basic Right, and basicLeft nodes. To clarify, these nodes are the invisible triangles (lowered opacity) the form a square, which I am using to test contact. – Christopher Hahn Feb 26 '20 at 22:15
  • This whole category bit mask stuff is just for performance. To debug this, just set the category for every node to 1 and the contactBitmask to 1. See if you get contacts -- if you do, then one-at-a-time, start using more categories to remove nodes that you don't care about. – Lou Franco Feb 26 '20 at 23:39
  • If you set all categories to 1, and you still don't get contacts, there is something else wrong – Lou Franco Feb 26 '20 at 23:39
  • @LouFranco I updated the code in the extension GameScene and I updated the code in the Settings.swift file. In the Settings.swift file, I did what you said and changed everything to 1. I have edited the code above, so you can see exactly what I put. I am still not getting any contact. – Christopher Hahn Feb 27 '20 at 03:37
  • @Lou Franco Should I add something like this to the spawnBasicBrick functions? What did you mean by set the contactBitmasks to 1? brickRight.physicsBody?.contactTestBitMask = PhysicsCategories.basicTopCategory | PhysicsCategories.basicBottomCategory | PhysicsCategories.basicLeftCategory | PhysicsCategories.basicRightCategory – Christopher Hahn Feb 27 '20 at 03:40
  • Categories and bitmasks are an optimization -- you don't need them to get things working. So .... set the category for every node to the number 1. Set the contactTestBitMask for every node to the number 1. Now test -- does it work? – Lou Franco Feb 27 '20 at 13:49
  • If it works -- really learn exactly what categories and contactTestBitMask do and start to introduce them slowly to your code (this will make the performance of the game better). Test EVERY SINGLE CHANGE you do. It should stay working – Lou Franco Feb 27 '20 at 13:50
  • If you set everything to 1 and your contact test code does not work, then (1) keep it that way until you find the other problem (2) try to figure out what else you are doing wrong. You should not introduce more categories and contactTestBitMask until you have everything working. Just use one category (set every category to the number 1) – Lou Franco Feb 27 '20 at 13:51
  • @LouFranco I have posted a new question called "Swift: No contact detection between nodes" on this website. The code on there has been updated, but I still don't receive contact detection. Any tips? – Christopher Hahn Mar 05 '20 at 02:37
  • https://stackoverflow.com/questions/60477859/swift-no-contact-detection-between-nodes?r=SearchResults – Lou Franco Mar 05 '20 at 09:46

1 Answers1

0

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
Lou Franco
  • 87,846
  • 14
  • 132
  • 192