Can't Understand How Collision Bit Mask Works

Can't understand how collision bit mask works

That is not how collision handling works. When two bodies are in intersection, physics engine performs logical AND operator between current's body collisionBitMask and other's body categoryBitMask:

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.

The source.

So the result depending on how you set categoryBitMask on those two bodies. A default value for categoryBitMask is 0xFFFFFFFF, means all bits set. So when you perform & between 0xFFFFFFFF and 0b10 or 0b01, the result would be non-zero value, thus the collision.

So for example, setting your bodies like this:

spriteA.physicsBody?.categoryBitMask = 0b01
spriteA.physicsBody?.collisionBitMask = 0b01

and

spriteB.physicsBody?.categoryBitMask = 0b10
spriteB.physicsBody?.collisionBitMask = 0b10

will give you the result you want. Also, this is probably not the exact setup you need, it is just a simple example, and you will have to change values according to your needs. In this case, spriteA will collide only with bodies which have categoryBitMask set to 0b01. Same goes for spriteB, it will collide with bodies which have categoryBitMask set to 0b10.

Also, in the case if you don't want these sprites to be able to collide with anything, simply set their collisionBitMask properties to 0.

How does collisionBitMask work? Swift/SpriteKit

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.

Game Engine Collison Bitmask... Why 0x01 etc?

The reason for the bitmasks is that it enables you / the program to easily and very quickly compute wether a collision between two objects occurs or does not occur. Therefore: yes it is some sort of optimization.

Assuming we have the three categories

  • missile 0x1 << 0
  • player 0x1 << 1
  • wall 0x1 << 2

Now we have a Player instance, its category is set to player. Its collision bitmask is set to missile | player | wall (+ instead of | works too) since we want to be able to collide with all three types: other players, the level walls and the bullets / missiles flying around.

Now we have a Missile with category set to missile and collision bitmask set to player | wall: it does not collide with other missiles but hits players and walls.

If we now want to evaluate wether two objects can collide with each other we take the category bitmask of the first one and the collision bitmask of the second one and simply & them:

The setup described above looks like the following in code:

let player : UInt8 = 0b1 << 0  // 00000001 = 1
let missile : UInt8 = 0b1 << 1 // 00000010 = 2
let wall : UInt8 = 0b1 << 2 // 00000100 = 4

let playerCollision = player | missile | wall // 00000111 = 7
let missileCollision = player | wall // 00000101 = 5

The subsequent reasoning is basically:

if player & missileCollision != 0 {
print("potential collision between player and missile") // prints
}
if missile & missileCollision != 0 {
print("potential collision between two missiles") // does not print
}

We are using some bit arithmetics here, each bit represents a category.
You could simply enumerate the bitmasks 1,2,3,4,5... but then you could not do any math on them. Because you do not know if a 5 as category bitmask is really a category 5 or it was an object of both categories 1 and 4.

However using only bits we can do just that: the only representation in terms of powers of 2 of a 7 is 4 + 2 + 1: therefore whatever object posses collision bitmask 7 collides with category 4, 2 and 1. And the one with bitmask 5 is exactly and only a combination of category 1 and 4 - there is no other way.

Now since we are not enumerating - each category uses one bit and the regular integer has only 32 (or 64) bits we can only have 32 (or 64) categories.

Take a look at the following and a bit more extensive code which demonstrates how the masks are used in a more general term:

let playerCategory : UInt8 = 0b1 << 0
let missileCategory : UInt8 = 0b1 << 1
let wallCategory : UInt8 = 0b1 << 2

struct EntityStruct {
var categoryBitmask : UInt8
var collisionBitmask : UInt8
}

let player = EntityStruct(categoryBitmask: playerCategory, collisionBitmask: playerCategory | missileCategory | wallCategory)
let missileOne = EntityStruct(categoryBitmask: missileCategory, collisionBitmask: playerCategory | wallCategory)
let missileTwo = EntityStruct(categoryBitmask: missileCategory, collisionBitmask: playerCategory | wallCategory)
let wall = EntityStruct(categoryBitmask: wallCategory, collisionBitmask: playerCategory | missileCategory | wallCategory)

func canTwoObjectsCollide(first:EntityStruct, _ second:EntityStruct) -> Bool {
if first.categoryBitmask & second.collisionBitmask != 0 {
return true
}
return false
}

canTwoObjectsCollide(player, missileOne) // true
canTwoObjectsCollide(player, wall) // true
canTwoObjectsCollide(wall, missileOne) // true
canTwoObjectsCollide(missileTwo, missileOne) // false

The important part here is that the method canTwoObjectsCollide does not care about the type of the objects or how many categories there are. As long as you stick with bitmasks that is all you need to determine wether or not two objects can theoretically collide (ignoring their positions, which is a task for another day).

Intermittent errors with Bitmask Collisions

I wouldn't be so sure that the problem is solved. When I tried your code, it crashed cause one of the nodes of contact bodies was nil. I was able to produce crash when creating a physics body from texture, from rectangle, with a circle. It doesn't really matter... The problem is not that :)

The cause of this, is that you are removing your nodes before physics simulations are done.

Take a look at how one frame looks like:

cycle

So what was happening is that you remove your nodes before physics simulation is done, so Physics engine retain physics body cause its needed to finish calculations, but node is removed.

And thus nil in didBegin. So the solution is to make a variable that will hold nodes for removal:

private var trash:[SKNode] = []

Then at every place you have node with physics body do this:

(say your playerHitAsteroid method)

trash.append(laserNode)
trash.append(astroidNode)

self.run(SKAction.wait(forDuration: 2)) {[weak self] in
guard let `self` = self else {return}
self.trash.append(explosion)
}

You have few more places to change this in application. Take a look a this part too:

if livesArray.count == 0 {
trash.append(playerNode)

print("Game over")
}

and some more. But when you fix it at all places like this, you are ready to implement actual removal by overriding didSimulatePhysics

 override func didSimulatePhysics() {
//first go through every node and remove it from parent
trash.map { node in
node.run(SKAction.sequence([SKAction.fadeOut(withDuration: 0.25), SKAction.removeFromParent()]))
}
trash.removeAll() // then empty thrash array before next frame
}

And finally you can change didBegin like this, just to catch this error immediately. Which won't happen if you follow this strategy which is:

  • Remove nodes with physics bodies in didSimulatePhysics
  • Set correctly bit masks (which as it seems, you have done correctly)

Maybe its worth of mention to be careful with Timer too. Check this out. It was long time ago, maybe something has changed, I didn't test it recently, but still I would prefer update method or SKAction for time related actions in my game.

So, change didBegin like this:

func didBegin(_ contact: SKPhysicsContact) {
guard let nodeA = contact.bodyA.node, let nodeB = contact.bodyB.node else {

fatalError("Physics body without its node detected!")
}
let A = contact.bodyA
let B = contact.bodyB

//PlayerLaser is A and Astroid is B
if (A.categoryBitMask & playerLaserCategory) != 0 && (B.categoryBitMask & astroidCategory) != 0 {
playerLaserHitAstroid(laserNode: A.node as! SKSpriteNode, astroidNode: B.node as! SKSpriteNode)
}
//PlayerLaser is A and Enemy is B
else if (A.categoryBitMask & playerLaserCategory) != 0 && (B.categoryBitMask & enemyCategory) != 0 {
playerLaserHitEnemy(laserNode: A.node as! SKSpriteNode, enemyNode: B.node as! SKSpriteNode)
}
//Player is A and Astroid is B
else if (A.categoryBitMask & playerCategory) != 0 && (B.categoryBitMask & astroidCategory) != 0 {
playerHitAstroid(playerNode: A.node as! SKSpriteNode, astroidNode: B.node as! SKSpriteNode)
}
//Player is A and Enemy is B
else if (A.categoryBitMask & playerCategory) != 0 && (B.categoryBitMask & enemyCategory) != 0 {
playerHitEnemy(playerNode: A.node as! SKSpriteNode, enemyNode: B.node as! SKSpriteNode)
}
}

Not needed, but I think this way of implementing contact detection (by switching mask) is a bit more readable, so if you want, take a peek.

One suggestion unrelated to this node removals... Don't use that much forced unwrappings! :D

swift skspritenodes coliding even thought there is no collision bit mask

you have

 static let circle : UInt32 = 0x1 << 1
static let obstacle : UInt32 = 0x1 << 1
static let score : UInt32 = 0x1 << 1

you need

static let circle : UInt32 = 0x1 << 0
static let obstacle : UInt32 = 0x1 << 1
static let score : UInt32 = 0x1 << 2

Contact and category bitmask - does only one, or both, need to be set?

Let me try to explain all these bit masks with an example from an actual game code. This game has the following objects: ball, pitch, ground, bat, boundary, batting stumps, bowling stumps which will have physics interactions.

Sorry the code is in ObjC, but translation to Swift is straightforward.

Step 1: Set up the category bit masks, which specify the type of each object


typedef NS_OPTIONS(NSUInteger, CollisionCategory) {
CollisionCategoryBall = 1 << 1,
CollisionCategoryPitch = 1 << 2,
CollisionCategoryGround = 1 << 3,
CollisionCategoryBat = 1 << 4,
CollisionCategoryBoundary = 1 << 5,
CollisionCategoryBattingStumps = 1 << 6,
CollisionCategoryBowlingStumps = 1 << 7,
};

Step 2: Now we decide which objects have collisions with each other.


For example for the ball object, there are collisions with pitch, ground bat and boundary. This is setup as follows, a bitwise OR operation, assuming ballBody is a SCNPhysicsBody:

ballBody.collisionBitMask =
(CollisionCategoryPitch | CollisionCategoryGround | CollisionCategoryBat |
CollisionCategoryBoundary);

Step 3: (Optionally), if you want to be notified when two physics bodies are in contact with each other.


Here, you set up the contactTestBitMask. For the ball for example, I want to be notified when it is in contact with pitch, ground, bat, boundary. Again, to do that:

ballBody.contactTestBitMask =
(CollisionCategoryPitch | CollisionCategoryGround | CollisionCategoryBat |
CollisionCategoryBoundary);

When you set up the contactTestBitMask, you will handle the contact notifications in the physics delegate, which will be something like this:

- (void)physicsWorld:(SCNPhysicsWorld*)world
didBeginContact:(SCNPhysicsContact*)contact {
CollisionCategory contactMask = contact.nodeA.physicsBody.categoryBitMask |
contact.nodeB.physicsBody.categoryBitMask;
if ((contactMask == (CollisionCategoryBall | CollisionCategoryPitch)) &&
!self.ballLandedOnPitch) {
NSLog(@" Ball hit the pitch");
}
...
}

So overall you just categorize your objects, then setup the collision mask which is just a bitwise OR operation of the category masks of the objects with which there will be collisions and optionally to handle the contacts of bodies with each other, set up the contact test bit mask.

But when it comes to contact testing, if A has its category mask set to 1 and contact bitmask set to 2,

I am not sure if you intend to explicitly set the contact bit mask to 2 above; the contact mask should be really be a bitwise OR operation of the category masks of the objects with which there will be contacts and you want to be notified of.

I think the docs are a bit confusing and hope the above explanation clears up your doubts.

Collision Bit Mask Hero passes right through EVERY thing, even the Ground

If hero has to detect collision with only the ground, then hero's collission bit mask should be

heroSprite.physicsBody?.collisionBitMask = GROUNDCATEGORY

To detect collision with ground and something else, you should use the OR | operator to combine both categoryBitMasks

 heroSprite.physicsBody?.collisionBitMask = GROUNDCATEGORY | SOMETHINGCATEGORY

The contactTestBitMask is used to get a callback on contact between the two bodies. We get a callback when both objects share the same space. It doesn't handle collisions.

I think what you need is contact detection with fire and coins and collision detection with ground. So just set collision bit mask of heroSprite to GROUNDCATEGORY like in my first code snippet.

let HEROCATEGORY: UInt32 = 0x1 << 1
let GROUNDCATEGORY: UInt32 = 0x1 << 2
let FIRECATEGORY: UInt32 = 0x1 << 3
let COINCATEGORY: UInt32 = 0x1 << 4
let NUMBERCATEGORY: UInt32 = 0x1 << 5

heroSprite.physicsBody!.categoryBitMask = HEROCATEGORY
heroSprite.physicsBody!.collisionBitMask = GROUNDCATEGORY // changed
heroSprite.physicsBody!.contactTestBitMask = GROUNDCATEGORY | FIRECATEGORY | COINCATEGORY | NUMBERCATEGORY

grassGround.physicsBody!.categoryBitMask = GROUNDCATEGORY
grassGround.physicsBody!.collisionBitMask = HEROCATEGORY

coinSprite.physicsBody!.categoryBitMask = COINCATEGORY
coinSprite.physicsBody!.contactTestBitMask = HEROCATEGORY

Swift detecting collisions and setting enums

If you want your code to be notified when ship contacts (not collides) either a mete, fuel or an alien, you should have:

ship.physicsBody!.contactTestBitMask = ColliderType.object.rawValue | ColliderType.fuel.rawValue | ColliderType.alien.rawValue

i.e. the contact Test bitmask is equal to object OR fuel OR alien.

Check out this simple Sprite-Kit project here on SO with contacts, collisions, touch events and a helper function (checkPhysics()) which will analyse your physics setup - this function can be dropped into any sprite-Kit project and called to show what contacts and collisions will occur between which physics bodies.

Attack button in SpriteKit

Additional info:

It's worth remembering that unless you specify otherwise, everything collides with everything else and nothing contacts anything.

i.e. every node's collisonBitMask is set to all 1s and its contactTestbitMask is set to all 0s.

Contacts or collisions between nodeA and nodeB can be turned off using:

nodeA.physicsBody?.collisionBitMask &= ~nodeB.category

We logically AND nodeA's bitMask with the inverse (logical NOT, the ~ operator) of nodeB's category bitmask to 'turn off' that bit nodeA's bitMask.

Contacts or collisions between nodeA and nodeC can be turned ON at any point using:

nodeA.physicsBody?.contactTestBitMask |= nodeC.category

We logically AND nodeA's bitMask with nodeC's category bitmask to 'turn on' that bit in nodeA's bitMask.

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
}


Related Topics



Leave a reply



Submit