0
votes

Okay, so I have a really specific problem and I hope I can help you. I will attach an image to clarify what I'm talking about.

clarification

redball.physicsBody is spawning above boss.physicsBody - falling (through) the boss. Colliding with whitebar.physicsBody - bouncing back up and once again colliding with boss.physicsBody. THIS time, i want to fire an event that notices when the ball is bouncing back up and hitting the boss.

Currently. Boss & Ball shares the same collisionBitMask, so that they can pass through each other. When the ball collides with the bar, I'm trying to add ball.physicsBody?.contactTestBitMask = PhysicsCategory.boss.rawValue to the ball. So that i can notice the collision on the way back up between ball & boss. However this does not seem to work.

Does anyone have a solution to this weird problem? Super thankful for some help.

Sidenote: If someone can figure out a clever title, let me know and ill edit it!

EDIT: Adding code

enum PhysicsCategory : UInt32 {
  case bar = 1
  case ball = 2
  case boss = 4
  case noCollision = 8
}

class GameScene: SKScene, SKPhysicsContactDelegate {

var score = Int()
var background = SKSpriteNode(imageNamed: "background.png")
var bar = SKSpriteNode(imageNamed: "bar.png")
var boss1 = SKSpriteNode(imageNamed: "boss1.png")

override func didMoveToView(view: SKView) {
    self.scene?.size = CGSize(width: 640, height:1136)
    physicsWorld.contactDelegate = self

    background.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
    background.zPosition = -20
    self.addChild(background)

    bar.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 8)
    bar.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: bar.size.width, height: bar.size.height))
    bar.physicsBody?.categoryBitMask = PhysicsCategory.bar.rawValue
    bar.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue
    bar.physicsBody?.collisionBitMask = PhysicsCategory.bar.rawValue
    bar.physicsBody?.affectedByGravity = false
    bar.physicsBody?.dynamic = false
    self.addChild(bar)

    boss1.position = CGPoint(x: self.frame.width, y: self.frame.height - boss1.size.height / 2)
    boss1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: boss1.size.width, height: boss1.size.height))
    boss1.physicsBody?.categoryBitMask = PhysicsCategory.boss.rawValue
    boss1.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue
    boss1.physicsBody?.collisionBitMask = PhysicsCategory.noCollision.rawValue
    boss1.physicsBody?.affectedByGravity = false
    boss1.physicsBody?.dynamic = false
    boss1.zPosition = 5
    self.addChild(boss1)

    boss1.runAction(SKAction.moveToX(self.frame.width / 2, duration: 1))

    let spawnBallsAction = SKAction.sequence([SKAction.waitForDuration(2), SKAction.runBlock(spawnBalls)])
    self.runAction(SKAction.repeatActionForever(spawnBallsAction))

}

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

    if (firstBody.categoryBitMask & PhysicsCategory.bar.rawValue == PhysicsCategory.bar.rawValue &&
        secondBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue) {

            CollisionWithBar(firstBody.node as! SKSpriteNode, ball: secondBody.node as! SKSpriteNode)

    }

    if (firstBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue &&
        secondBody.categoryBitMask & PhysicsCategory.boss.rawValue == PhysicsCategory.boss.rawValue) {

            CollisionWithBoss(firstBody.node as! SKSpriteNode, boss: secondBody.node as! SKSpriteNode)

    }

}

func CollisionWithBar(bar: SKSpriteNode, ball: SKSpriteNode) {
    ball.physicsBody?.applyImpulse(CGVectorMake(0, 500))
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.boss.rawValue //Trying to solve the problem, aint working
}

func CollisionWithBoss(ball: SKSpriteNode, boss: SKSpriteNode) {
    NSLog("Ball hit the boss")
}


func spawnBalls(){

    let ball = SKSpriteNode(imageNamed: "ball.png")
    let MinValue = self.frame.width / 8
    let MaxValue = self.frame.width - 20
    let SpawnPoint = UInt32(MaxValue - MinValue)
    ball.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.frame.height - 128)
    ball.zPosition = 50

    //Physics
    ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.frame.height / 2)
    ball.physicsBody?.categoryBitMask = PhysicsCategory.ball.rawValue
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue
    ball.physicsBody?.collisionBitMask = PhysicsCategory.noCollision.rawValue
    ball.physicsBody?.affectedByGravity = true
    ball.physicsBody?.dynamic = true

    self.addChild(ball)
}
1
when you say add, do you mean ball.physicsBody?.contactTestBitMask = PhysicsCategory.boss.rawValue | PhysicsCategory.whitebar.rawValue. also, are they sharing the same categoryBitMask? because they should not be sharing the same collisionBitMask, you should have the collisionBitMask not look at boss on the start, then when it hits the white paddle, activate itKnight0fDragon
Can you edit your post with the code that pertains to your problem? It will help to see how you are creating the 3 nodes and their physics bodies along with what happens when the ball first hits the white bar and the test you are doing to see if the ball is colliding with the boss.Trent Sartain
@TrentSartain I added all the code. I'm also trying KnightOfDragons idea, comming back soon!Glutch
@Knight0fDragon If i do not use the same collisionBitMask on both ball & boss, the balls does not go through the boss? They get stuck and roll on the sides. Maybe i just dont understand your explanation, im sorry. I also added the code so you can check it outGlutch
OK you have a few issues to worry about here, 1 being that firstBody can either be bar or ball, the order is not preserved, so you need to handle that, otherwise you may hit a condition where firstBody is ball and secondBody is bar, and nothing will happenKnight0fDragon

1 Answers

1
votes

This is how contacts should be done:

Upon creating a ball set the contact category to this:(which you have)

ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue

Make sure the boss contacts nobody:

boss1.physicsBody?.contactTestBitMask = PhysicsCategory.noCollision.rawValue

Then on Contact, you want to do this:

func didBeginContact(contact: SKPhysicsContact) {
    let firstBody : SKPhysicsBody = (contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB
    let secondBody : SKPhysicsBody = (contact.bodyA.categoryBitMask >= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB

    if (firstBody.categoryBitMask & PhysicsCategory.bar.rawValue ==  PhysicsCategory.bar.rawValue &&
     secondBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue) {
            //This should be inside CollisionWithBar
            ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue | PhysicsCategory.boss.rawValue

            //the ball has hit the bar, so lets enable hitting on the boss
            //normally we would only want to do this once, but since this is 
            //tiny, it would be more time to wrap it in ifs and putting guards on it
            boss1.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue
            CollisionWithBar(firstBody.node as! SKSpriteNode, ball: secondBody.node as! SKSpriteNode)

    }

    if (firstBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue &&
    secondBody.categoryBitMask & PhysicsCategory.boss.rawValue == PhysicsCategory.boss.rawValue) {
            //This should be inside CollisionWithBoss
            ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue

            CollisionWithBoss(firstBody.node as! SKSpriteNode, boss: secondBody.node as! SKSpriteNode)

    }

 }

Which basically says, if the ball hits the bar, enable boss hitting, if the ball hits the boss, disable boss hitting

Edit: Upon the realization of having multiple balls, we have to change some things

Make sure the boss contacts ball upon creation again:

boss1.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue

Now we need to create a new category

enum PhysicsCategory : UInt32 {
    case bar = 1
    case ball = 2
    case boss = 4
    case noCollision = 8
    case initialBall = 16
}

In the beginning, assign the category of ball to this:

ball.physicsBody?.categoryBitMask = initialBall

Then your CollissionWithBar will look like this:

func CollisionWithBar(bar: SKSpriteNode, ball: SKSpriteNode) {
    ball.physicsBody?.applyImpulse(CGVectorMake(0, 500))
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue | PhysicsCategory.boss.rawValue


    ball.physicsBody?.categoryBitMask = PhysicsCategory.ball.rawValue 
}

And your CollissionWithBolls will look like this:

func CollisionWithBoss(ball: SKSpriteNode, boss: SKSpriteNode) {
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.bar.rawValue
}

Finally, on Contact, you want to do this:

func didBeginContact(contact: SKPhysicsContact) {
    let firstBody : SKPhysicsBody = (contact.bodyA.categoryBitMask <= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB
    let secondBody : SKPhysicsBody = (contact.bodyA.categoryBitMask >= contact.bodyB.categoryBitMask) ? contact.bodyA : contact.bodyB

    if (firstBody.categoryBitMask & PhysicsCategory.bar.rawValue ==  PhysicsCategory.bar.rawValue &&
     secondBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue) {
            CollisionWithBar(firstBody.node as! SKSpriteNode, ball: secondBody.node as! SKSpriteNode)

    }

    if (firstBody.categoryBitMask & PhysicsCategory.ball.rawValue == PhysicsCategory.ball.rawValue &&
    secondBody.categoryBitMask & PhysicsCategory.boss.rawValue == PhysicsCategory.boss.rawValue) {


            CollisionWithBoss(firstBody.node as! SKSpriteNode, boss: secondBody.node as! SKSpriteNode)

    }

 }