For this example, we will used 4 bodies and will show only the last 8 bits of the bit masks for simplicity. The 4 bodies are 3 SKSpriteNodes, each with a physics body and a boundary:
let edge = frame.insetBy(dx: 0, dy: 0)
physicsBody = SKPhysicsBody(edgeLoopFrom: edge)
Note that the 'edge' physics body is the physics body of the scene, not a node.
We define 4 unique categories
let purpleSquareCategory: UInt32 = 1 << 0 // bitmask is ...00000001
let redCircleCategory: UInt32 = 1 << 1 // bitmask is ...00000010
let blueSquareCategory: UInt32 = 1 << 2 // bitmask is ...00000100
let edgeCategory: UInt32 = 1 << 31 // bitmask is 10000...00000000
Each physics body is assigned the categories that it belongs to:
//Assign our category bit masks to our physics bodies
purpleSquare.physicsBody?.categoryBitMask = purpleSquareCategory
redCircle.physicsBody?.categoryBitMask = redCircleCategory
blueSquare.physicsBody?.categoryBitMask = blueSquareCategory
physicsBody?.categoryBitMask = edgeCategory // This is the edge for the scene itself
If a bit in a body's collisionBitMask is set to 1, then it collides (bounces off) any body that has a '1' in the same position in it's categoryBitMask. Similarly for contactTestBitMask.
Unless you specify otherwise, everything collides with everything else and no contacts are generated (your code won't be notified when anything contacts anything else):
purpleSquare.physicsBody.collisonBitMask = 11111111111111111111111111111111 // 32 '1's.
Every bit in every position is '1', so when compared to any other categoryBitMask, Sprite Kit will find a '1' so a collision will occur. If you do not want this body to collide with a certain category, you will have to set the correct bit in the collisonBitMask to '0'
and its contactTestbitMask
is set to all 0
s:
redCircle.physicsBody.contactTestBitMask = 00000000000000000000000000000000 // 32 '0's
Same as for collisionBitMask, except reversed.
Contacts or collisions between bodies can be turned off (leaving existing contact or collision unchanged) using:
nodeA.physicsBody?.collisionBitMask &= ~nodeB.category
We logically AND nodeA's collision bit mask with the inverse (logical NOT, the ~ operator) of nodeB's category bitmask to 'turn off' that bit nodeA's bitMask. e.g to stop the red circle from colliding with the purple square:
redCircle.physicsBody?.collisionBitMask = redCircle.physicsBody?.collisionBitMask & ~purpleSquareCategory
which can be shortened to:
redCircle.physicsBody?.collisionBitMask &= ~purpleSquareCategory
Explanation:
redCircle.physicsBody.collisonBitMask = 11111111111111111111111111111111
purpleSquareCategory = 00000000000000000000000000000001
~purpleSquareCategory = 11111111111111111111111111111110
11111111111111111111111111111111 & 11111111111111111111111111111110 = 11111111111111111111111111111110
redCircle.physicsBody.collisonBitMask now equals 11111111111111111111111111111110
redCircle no longer collides with bodies with a category of ....0001 (purpleSquare)
Instead of turning off individual bits in the collsionsbitMask, you can set it directly:
blueSquare.physicsBody?.collisionBitMask = (redCircleCategory | purpleSquareCategory)
i.e. blueSquare.physicsBody?.collisionBitMask = (....00000010 OR ....00000001)
which equals blueSquare.physicsBody?.collisionBitMask = ....00000011
blueSquare will only collide with bodies with a category or ..01 or ..10
Contacts or collisions between 2 bodies can be turned ON (without affecting any existing contacts or collisions) at any point using:
redCircle.physicsBody?.contactTestBitMask |= purpleSquareCategory
We logically AND redCircle's bitMask with purpleSquare's category bitmask to 'turn on' that bit in redcircle's bitMask. This leaves any other bits in redCircel's bitMas unaffected.
You can make sure that every shape 'bounces off' a screen edge as follows:
// Make sure everything collides with the screen edge
enumerateChildNodes(withName: "//*") { node, _ in
node.physicsBody?.collisionBitMask |= self.edgeCategory //Add edgeCategory to the collision bit mask
}
Note:
Collisions can be one-sided i.e. object A can collide (bounce off) object B, whilst object B carries on as though nothing had happened. If you want 2 object to bounce off each other, they must both be told to collide with the other:
blueSquare.physicsBody?.collisionBitMask = redCircleCategory
redcircle.physicsBody?.collisionBitMask = blueSquareCategory
Contacts however are not one-sided; if you want to know when object A touched (contacted) object B, it is enough to set up contact detection on object A with regards to object B. You do not have to set up contact detection on object B for object A.
blueSquare.physicsBody?.contactTestBitMask = redCircleCategory
We don't need redcircle.physicsBody?.contactTestBitMask= blueSquareCategory
Advanced usage:
Not covered here, but physics bodies can belong to more than one category. E.g. we could set our game up as follows:
let squareCategory: UInt32 = 1 << 0 // bitmask is ...00000001
let circleCategory: UInt32 = 1 << 1 // bitmask is ...00000010
let blueCategory: UInt32 = 1 << 2 // bitmask is ...00000100
let redCategory: UInt32 = 1 << 3 // bitmask is ...00001000
let purpleCategory: UInt32 = 1 << 4 // bitmask is ...00010000
let edgeCategory: UInt32 = 1 << 31 // bitmask is 10000...0000000
Each physics body is assigned the categories that it belongs to:
//Assign our category bit masks to our physics bodies
purpleSquare.physicsBody?.categoryBitMask = squareCategory | purpleCategory
redCircle.physicsBody?.categoryBitMask = circleCategory | redCategory
blueSquare.physicsBody?.categoryBitMask = squareCategory | blueCategory
their categorybitMasks are now:
purpleSquare.physicsBody?.categoryBitMask = ...00010001
redCircle.physicsBody?.categoryBitMask = ...00001010
blueSquare.physicsBody?.categoryBitMask = ...00000101
This will affect how you manipulate the bit fields. It can be useful (for example) to indicate that a physics body (e.g. a bomb) has changed somehow (e.g. it might have gained the 'super' ability which is another category, and you might check that a certain object (an alien mothersh