Stop objects from colliding using SpriteKit
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
and bowlingball
. Now, obviously, we want the basketball
and bowlingball
to collide with the each other. So you set the collisionBitMasks like so:
basketball.physicsBody?.collisionBitMask = UInt32(2)
bowlingball.physicsBody?.collisionBitMask = UInt32(2)
Great. Now, we want the bowlingball
to sink to the bottom of the pool
, and the basketball
to collide with the pool
(might be more of a splash, but bear with me). How would we do this? We could try:
pool.physicsBody?.collisionBitMask = UInt32(2) // ?
But wait, that would make the basketball
AND the bowlingball
collide with the pool
. We only want the basketball
to collide with the pool , whereas we want the bowlingball
to ignore the pool
and sink straight to the bottom with no collisions. This is where categoryBitMask
s come in handy:
let basketballBitMask = UInt32(1)
let bowlingballBitMask = UInt32(2)
let poolBitMask = UInt32(4) // Why 4? See next section
basketball.physicsBody?.categoryBitMask = basketballBitMask
bowlingball.physicsBody?.categoryBitMask = bowlingballBitMask
pool.physicsBody?.categoryBitMask = poolBitMask
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:
// basketball physics body collides with bowlingball(2) OR pool(4)
basketball.physicsBody?.collisionBitMask = bowlingballBitMask | poolBitMask
// ( '|' = logical OR operator)
// bowlingball physics body only collides with basketball(1)
bowlingball.physicsBody?.collisionBitMask = basketballBitMask
// pool physics body only collides with basketball(1)
pool.physicsBody?.collisionBitMask = basketballBitmask
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 the pool
, the SpriteKit physics engine will AND ('&') together the bowlingball
's categoryBitMask with the pool
's collisionBitMask (or vice versa; the result is the same):
objectsShouldCollide = (bowlingball.physicsBody?.categoryBitMask &
pool.physicsBody?.collisionBitMask)
// objectsShouldCollide = (ob010 & 0b100) = 0b000
Because the bowlingball
's categoryBitMask
and the pool
's collisionBitMask
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:
objectsShouldCollide = (0b11111111111111111111111111111111 & // Default categoryBitMask for "object"
0b00000000000000000000000000000001) // collisionBitMask for "second" object
// = 0b00000000000000000000000000000001
So when you haven't defined the object
's categoryBitMask, no matter what you set as the second
object's collisionBitMask
, 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:
bowlingball.physicsBody?.collisionBitMask = basketballBitMask | bowlingballBitMask
// bowlingball collision bit mask (in binary) = 0b10 | 0b01 = 0b11
// bowlingball collision bit mask (in decimal) = 2 | 1 = 3
Here you can see that if we had set the pool
s physicsCategory to UInt32(3), it would no longer be distinguishable from a bowlingball
or basketball
.
Further suggestions
Learn to name variables with purpose, even if you're just using them for testing (although, coincidentally, "object
and second
object" worked quite well).
Use a struct for bitmasks to simplify your code and improve readability:
struct PhysicsCategory {
static let Obj1 : UInt32 = 0b1 << 0
static let Obj2 : UInt32 = 0b1 << 1
static let Obj3 : UInt32 = 0b1 << 2
static let Obj4 : UInt32 = 0b1 << 3
}
obj1.physicsBody?.categoryBitmask = PhysicsCategory.Obj1 // etc
Is it possible to deactivate collisions in physics bodies in spriteKit?
Check out collisionBitMask, categoryBitMask, and contactTestBitMask in the SKPhysicsBody
class.
Essentially, physics bodies with the same collisionBitMask
value will "pass-through" each other.
- Correction: If the category and collision bits match, they will interact. If they do not match, those two will not interact. And if the collision bits, and category bits, are both zero, of course that item will interact with nothing whatsoever.
Then, you set the categoryBitMask
and contactTestBitMask
values to create an SKPhysicsContact
Object on contact. Finally, your Class should adopt the SKPhysicsContactDelegate
protocol. Use the - didBeginContact:
method to detect and handle the SKPhysicsContact
object.
static const uint8_t heroCategory = 1;
static const uint8_t foodCategory = 2;
--
food.physicsBody.categoryBitMask = foodCategory;
food.physicsBody.contactTestBitMask = heroCategory;
food.physicsBody.collisionBitMask = 0;
--
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.contactTestBitMask = foodCategory;
hero.physicsBody.collisionBitMask = 0;
--
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody = contact.bodyA;
SKPhysicsBody *secondBody = contact.bodyB;
}
Stop Sprites from Pushing Each Other in SpriteKit
Set a collisionBitMask
to 0, so all collisions of this particular object to others will be ignored.
Temporarily deactivate physical collision between two Nodes(Swift 2, IOS)
Turn off object2 collision by default. (Do this with collisionBitMask = 0)
Enable object contactBitMask with the flag for object1.
On the didEndContact Method, enable the collision object2 for object1 when the condition of object1 contacting object2 is met.
This will allow you to avoid things like timers and checking the update loop constantly.
In English, you are saying: When object 2 no longer is touching object 1, object 2 is now able to collide with object 1.
Depending on circumstance, you may want to remove the contact test after you enable collision
Objects colliding with each-other when they shouldn't (SpriteKit)
Moving this from my comment.
Apple's SpriteKit physics example may be of interest.
You are using integer values, not binary values. Here is what your values look like in binary:
everythingElseCategory = 0011b // I think you need the "b" somewhere to indicate binary
object1Category = 0010b
object2Category = 0100b
Basically, SK physics will use the &
(binary and) operator on the two objects and if the value is not 0
, they will collide. Take for example your two objects:
object1.physicsBody?.categoryBitMask = object1 (0010)
object1.physicsBody?.collisionBitMask = everythingElseCategory (0011)
object2.physicsBody?.categoryBitMask = object2 (0100)
object2.physicsBody?.collisionBitMask = everythingElseCategory (0011)
object1
's bit mask will collide with object2
's because 0010 & 0011 == 0010
or nonzero. object2
's bit mask won't collide with object1
's because 0100 & 0011 == 0000
a zero or no collision.
The easiest way to fix this is to use bit shifting. The syntax looks like this:
value = bitValue << placesToShiftLeft
So your bit mask setting would look like this:
everythingElseCategory = 1 << 0 // I have this shifted by 0 just to show that you can do that
object1Category = 1 << 1
object2Category = 1 << 2
Now when you set your bit masks to your objects, use the |
(binary or) operator to combine them. 001 | 010 == 011
is how it works.
object1.physicsBody?.categoryBitMask = object1Category
object1.physicsBody?.collisionBitMask = everythingElseCategory | object2Category
object2.physicsBody?.categoryBitMask = object2Category
object2.physicsBody?.collisionBitMask = everythingElseCategory | object1Category
everythingElse.physicsBody?.categoryBitMask = everythingElseCategory | object1Category | object2Category
This will prevent multiple object1
's from colliding with each other, but allow colliding with everything else.
prevent Spritekit nodes with the same Catagorymask from colliding
It's bit mask in binary.
So it's better to use some bit operator:
enum CategoryMask : UInt32 {
case playeragain = 0b00000001
case player = 0b00000010
case GBullet = 0b00000100
case BBullet = 0b00001000
case enemyships = 0b00010000
case coin = 0b00100000
case boss = 0b01000000
}
// case playeragain = 1<<0 //1
// case player = 1<<1 // 2
// case GBullet = 1<<2 // 4
// case BBullet = 1<<3 //8
// case enemyships = 1<<4 //16
// case coin = 1<<5 //32
// case boss = 1<<6 //64
//
This should give you what you really what.
It's an example from Apple Document and redball will hit ground and blue ball will not.
let ground = SKShapeNode()
redBall.physicsBody?.collisionBitMask = 0b0001
blueBall.physicsBody?.collisionBitMask = 0b0010
ground.physicsBody?.categoryBitMask = 0b0001
Detect Sprite Collision without bouncing off in SpriteKit
Thee default behavior of spritekit is that everything collides with everything unless the Collision bit mask is change to 0.
Change this value in your code to 0 in all those objects that you do not want to bounce off but receive notifications from them.
player = SKSpriteNode(texture: playerTexture)
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.height / 2)
player.physicsBody?.dynamic = true
player.physicsBody?.allowsRotation = false
player.physicsBody?.categoryBitMask = playerCategory
player.physicsBody?.contactTestBitMask = coinCategory
player.physicsBody?.collisionBitMask = 0
var coin:SKSpriteNode = SKSpriteNode(texture: coinTexture)
coin.physicsBody = SKPhysicsBody(circleOfRadius: coin.size.height / 2)
coin.physicsBody?.dynamic = false
coin.physicsBody?.allowsRotation = false
coin.physicsBody?.categoryBitMask = coinCategory
coin.physicsBody?.contactTestBitMask = playerCategory
coin.physicsBody?.collisionBitMask = 0
This will stop those objects from bouncing against each other.
Related Topics
How to Add "%" to Data in iOS-Chart
What's the Difference Between a Required Initializer and a Designated Initializer
How to Insert an Image Inline Uilabel in iOS 8 Using Swift
Xcode Error: Missing Required Module 'Firebase'
Truncatingremainder VS Remainder in Swift
How to Call Initializer for Subclass of Generic Type
Variable P Passed by Reference Before Being Initialized
How to Pass Protocol with Associated Type (Generic Protocol) as Parameter in Swift
iOS - Arkit Node Disappear After 100M
Nsuserdefaults in Swift - Implementing Type Safety
Subclassing VS Extension in Swift
Difference Between Switch Cases "@Unknown Default" and "Default" in Swift 5
"Nsurl" Is Not Implicitly Convertible to "Url"; Did You Mean to Use "As" to Explicitly Convert
How to Save Cgimage to Data in Swift