How does collisionBitMask work? Swift/SpriteKit

As far as I'm aware the default for Physics bodies is to bounce off each other when they hit each other until you set their collisionBitMask to an equal number.

However, I'm having a huge issue accomplishing what seems like it should be very simple because of collisionBitmasks I believe.

let RedBallCategory  : UInt32 = 0x1 << 1
let GreenBallCategory: UInt32 = 0x1 << 2
let RedBarCategory : UInt32 = 0x1 << 3
let GreenBarCategory : UInt32 = 0x1 << 4
let WallCategory : UInt32 = 0x1 << 5


greenBall.physicsBody?.categoryBitMask = GreenBallCategory
greenBall.physicsBody?.contactTestBitMask = RedBarCategory
greenBall.physicsBody?.collisionBitMask = GreenHealthCategory

redBall.physicsBody?.categoryBitMask = RedBallCategory
redBall.physicsBody?.contactTestBitMask = GreenBarCategory
redBall.physicsBody?.collisionBitMask = RedHealthCategory

let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = borderBody
self.physicsBody?.friction = 0
borderBody.contactTestBitMask = RedBallCategory | GreenBallCategory
borderBody.categoryBitMask = WallCategory

So here I've got my 2 balls and my border body. I can get collision detection which I want, but when I add the border body's category bit mask, it allows the balls to both go through and off the screen, which I don't want.

I also want the balls to bounce off each other, but only when I comment out one of the ball's categoryBitMasks do they bounce. Otherwise they go through each other.

That makes absolutely no sense to me because each of these items has a different collisionbitmask. I also had it sometimes where setting all the numbers equal to 5 would allow everything to pass through each other, but then setting it all to 6 would allow everything to hit each other.

How exactly do collision bitmasks work and is there a proper way to manage a lot of crisscrossing collision rules?


Solution 1:

You can't get desired behaviour because you haven't set category, contact and collision bit masks properly. Here is an example on how you can set this to work:

greenBall.physicsBody?.categoryBitMask = GreenBallCategory //Category is GreenBall
greenBall.physicsBody?.contactTestBitMask = RedBarCategory | WallCategory //Contact will be detected when GreenBall make a contact with RedBar or a Wall (assuming that redBar's masks are already properly set)
greenBall.physicsBody?.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory //Collision will occur when GreenBall hits GreenBall, RedBall or hits a Wall

redBall.physicsBody?.categoryBitMask = RedBallCategory //Category is RedBall
redBall.physicsBody?.contactTestBitMask = GreenBarCategory | GreenBallCategory | WallCategory //Contact will be detected when RedBall make a contact with GreenBar , GreenBall or a Wall
redBall.physicsBody?.collisionBitMask = RedBallCategory | GreenBallCategory | WallCategory //Collision will occur when RedBall meets RedBall, GreenBall or hits a Wall

let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = borderBody
self.physicsBody?.friction = 0
borderBody.contactTestBitMask = RedBallCategory | GreenBallCategory //Contact will be detected when red or green ball hit the wall
borderBody.categoryBitMask = WallCategory
borderBody.collisionBitMask = RedBallCategory | GreenBallCategory // Collisions between RedBall GreenBall and a Wall will be detected

I would recommend you to read docs about categoryBitMask which is a mask that defines which categories a physics body belongs to:

Every physics body in a scene can be assigned to up to 32 different categories, each corresponding to a bit in the bit mask. You define the mask values used in your game. In conjunction with the collisionBitMask and contactTestBitMask properties, you define which physics bodies interact with each other and when your game is notified of these interactions.

contactTestBitMask - A mask that defines which categories of bodies cause intersection notifications with a current physics body.

When two bodies share the same space, each body’s category mask is tested against the other body’s contact mask by performing a logical AND operation. If either comparison results in a nonzero value, an SKPhysicsContact object is created and passed to the physics world’s delegate. For best performance, only set bits in the contacts mask for interactions you are interested in.

collisionBitmask - A mask that defines which categories of physics bodies can collide with this physics body.

When two physics bodies contact each other, a collision may occur. This body’s collision mask is compared to the other body’s category mask by performing a logical AND operation. If the result is a nonzero value, this body is affected by the collision. Each body independently chooses whether it wants to be affected by the other body. For example, you might use this to avoid collision calculations that would make negligible changes to a body’s velocity.

So basically, to set up all these, you should ask your self something like:

Okay, I have a green ball, and a red ball , and wall objects on the scene. Between which bodies I want collisions to occur, or when I want to register contacts? I want a green and a red ball to collide with each other and to collide against the walls. Not a problem. I will first set up categories properly, and then I will set collision bit masks like this:

greenBall.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory; //greenBall will collide with greenBall, redBall and a wall
redBall.collisionBitMask = GreenBallCategory | RedBallCategory | WallCategory

wall.collisionBitMask = GreenBall | RedBall

Now, I want to detect when some contacts occur (in didBeginContact: method)... But I don't want to get notified about all possible contacts, but rather to be notified just about contacts between balls (contacts between balls and a wall will be ignored). So lets set contactTestBitMasks to achieve this:

greenBall.contactTestBitMask = GreenBallCategory | RedBallCategory;
redBall.contactTestBitMask = GreenBallCategory | RedBallCategory;

And that's it. The important thing is when you not using contact detection, you should not set contactTestBitMask. This is because of performance reasons. If you don't need collision detection, and you are interested only in detecting contacts, you can set collisionBitMask = 0.

Important:

Make sure you have set physics world's contact delegate in order to use didBeginContact and didEndContact methods:

self.physicsWorld.contactDelegate = self; //where self is a current scene

Hope this helps a bit.

Solution 2:

Just want to add something here, that if the contact or collision are not able to work properly, you need to make sure that you assign the physicsBody shape first before applying the bitMasks.

Example of not working bitmask:

        SKSpriteNode *bird = [SKSpriteNode spriteNodeWithImageNamed:@"bird1.png"];
        bird.name = @"bird";
        bird.physicsBody.categoryBitMask = birdCategory;
        bird.physicsBody.contactTestBitMask = wallCategory | scoreCategory;
        bird.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bird.size.width/2.7];
        bird.position = CGPointMake(_mySize.width/3.0, 2*_mySize.height/3.0);
        [self addChild:bird];

And working one:

    SKSpriteNode *bird = [SKSpriteNode spriteNodeWithImageNamed:@"bird1.png"];
    bird.name = @"bird";
    bird.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:bird.size.width/2.7];
    bird.physicsBody.categoryBitMask = birdCategory;
    bird.physicsBody.contactTestBitMask = wallCategory | scoreCategory;
    bird.position = CGPointMake(_mySize.width/3.0, 2*_mySize.height/3.0);
    [self addChild:bird];

Anyway probably just a newbie mistake, but I'd share here cause this is what caused mine not to work properly.