Stop Objects from Colliding Using Spritekit

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 categoryBitMasks 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' categoryBitMasks. 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 bowlingballs 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 pools 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



Leave a reply



Submit