I am testing out the features of SpriteKit and I ran into a problem. I was reading into bit masks, colliding, category, and contact. I get what they are, mostly at least, I don't get the point of category bitmasks, but I get colliding bitmasks which are the ones I need to solve my problem.
Ok so my problem is I have two different types of sprites: object and second. The names don't really make much sense but it is just for the sake of testing. I want second to have an impulse, and I want object to have a force. I was able to apply the respective vectors on the sprites, but I do not want them to collide with each other. I want them to pass right through and ignore the existence of each other.
I tried to solve that issue by assigning different collision bitmasks to each other:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let texture = SKTexture(imageNamed: "pokeball")
let object = SKSpriteNode(texture: texture)
object.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: object.size.width,height: object.size.height))
object.physicsBody?.affectedByGravity = false
object.yScale = 0.5
object.xScale = 0.5
for t in touches {
object.position = t.location(in: self)
}
self.addChild(object)
object.physicsBody?.collisionBitMask = UInt32(4)
object.physicsBody?.applyForce(CGVector(dx: 0, dy: 10))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let texture = SKTexture(imageNamed: "purple")
let second = SKSpriteNode(texture: texture)
let impulse : Double = 20
let x = (impulse * Double(cosf(45)))
let y = Double(impulse * Double(sinf(45)))
let vector = CGVector(dx: x, dy: y)
second.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: second.size.width,height: second.size.height))
second.yScale = 1.5
second.xScale = 1.5
second.physicsBody?.isDynamic = true
for t in touches {
second.position = t.location(in: self)
}
self.addChild(second)
second.physicsBody?.collisionBitMask = UInt32(1)
second.physicsBody?.applyImpulse(vector)
}
So object has a bitmask of 4:
object.physicsBody?.collisionBitMask = UInt32(4)
And second has a bitmask of 1:
second.physicsBody?.collisionBitMask = UInt32(1)
I ran the simulator and they are still colliding with each other, so I went online and tried to look for some answers: I found one that says I must use numbers like:
these are bitmasks, you can't use arbitrary numbers 1,2,3,4,5 - you must use 1,2,4,8,16 and so on –
Can someone explain why? However, that wasn't the issue because I was using 1 and 4
Next question I ran into said that I had to use binary numbers (0100) and (0010), I tried them, same issue: still colliding.
I will leave a picture of the collisions: Collisions
Does anyone know why this is happening? My apologies in advance if this is a really dumb mistake or something that has already been asked, I just couldn't find it.
Why don't you download and play with this simple Sprite-Kit project? It creates various geometric shapes, sets some of them to collide, some to contact, uses a
checkPhysics()
function to show what's going to happen and then lets you flick the shapes around.Attack button in SpriteKit
Any questions as to it's workings and I'll be more than happy to try and explain.
There is a lot of documentation on these topics, but here is a practical example.
The power of categoryBitMasks
Pretend you have a collection of three nodes
pool
,basketball
andbowlingball
. Now, obviously, we want thebasketball
andbowlingball
to collide with the each other. So you set the collisionBitMasks like so:Great. Now, we want the
bowlingball
to sink to the bottom of thepool
, and thebasketball
to collide with thepool
(might be more of a splash, but bear with me). How would we do this? We could try:But wait, that would make the
basketball
AND thebowlingball
collide with thepool
. We only want thebasketball
to collide with the pool , whereas we want thebowlingball
to ignore thepool
and sink straight to the bottom with no collisions. This is wherecategoryBitMask
s come in handy:Because each object has a unique number assigned to it, you can now pick and choose which objects you'd like another object to collide with:
If you're not sure what the strange '|' symbol is doing, I highly recommend the swift documentation on advanced operators to help you understand what's happening here.
Why not just use collisionBitMasks?
Okay so we've set some bit masks. But how are they used? If we only have two objects why can't we just compare collisionBitMasks?
Simply put, that's just not how it works. When a
bowlingball
comes into contact with thepool
, the SpriteKit physics engine will AND ('&') together thebowlingball
's categoryBitMask with thepool
's collisionBitMask (or vice versa; the result is the same):Because the
bowlingball
'scategoryBitMask
and thepool
'scollisionBitMask
have zero bits in common,objectsShouldCollide
is equal to zero, and SpriteKit will stop the objects from colliding.But, in your case, you're not setting your objects'
categoryBitMask
s. So they have a default value of 2^32, or 0xFFFFFFFF (hexadecimal representation) or in binary, 0b11111111111111111111111111111111. So when an "object" hits a "second" object, SpriteKit does this:So when you haven't defined the
object
's categoryBitMask, no matter what you set as thesecond
object'scollisionBitMask
, objectsShouldCollide will never be zero, and they will always collide.Note: you could set an object's
collisionBitMask
to 0; but then that object would never be able to collide with anything.Using powers of 2 (0,1,2,4,8, etc.) for categoryBitMasks
Now let's say we wanted to include multiple
bowlingball
s that collided with each other. Easy:Here you can see that if we had set the
pool
s physicsCategory to UInt32(3), it would no longer be distinguishable from abowlingball
orbasketball
.Further suggestions
Learn to name variables with purpose, even if you're just using them for testing (although, coincidentally, "
object
andsecond
object" worked quite well).Use a struct for bitmasks to simplify your code and improve readability: