Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't understand how collision bit mask works

I am trying to use collision bit masks and contact test bit masks in Swift, I want two objects not to collide together, so I am doing :

firstNode.physicsBody?.collisionBitMask = 0b01
secondNode?.collisionBitMask = 0b10

Since SpriteKit does an AND operation on those two numbers, shouldn't the result be 00 since 10 & 01 = 00 ?

So why are collisions happening anyway ?

Thank you.

like image 613
Pop Flamingo Avatar asked Aug 21 '16 12:08

Pop Flamingo


2 Answers

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.

like image 114
Whirlwind Avatar answered Oct 22 '22 12:10

Whirlwind


That's not how you test interaction between nodes with collision bits.

  • The categoryBitMask is the category of the object.
  • The collisionBitMask is what the object responds to when collided with.
  • The contactTestBitMask is used for notifications when intersection occurs for the specified bit mask.

Suppose I have the following:

struct PC {
    static var player: UInt32 = 0b10 //2
    static var enemy: UInt32 = 0b100 //4
    static var rock: UInt32 = 0b1000 //8
}

player.physicsBody!.categoryBitMask = PC.player
player.physicsBody!.collisionBitMask = PC.enemy | PC.rock
enemy.physicsBody!.categoryBitMask = PC.enemy
enemy.physicsBody!.collisionBitMask = PC.player

So when you are checking when interaction occurs in the didBeginContact function, you check if their interaction occurred by using bit logic.

func didBeginContact(contact: SKPhysicsCountact) {
    //1
    let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

    //2
    if collision == PC.player | PC.enemy {
        //An interaction occured between the player and enemy.
    }
  1. The variable collision is using bitwise OR, which is |. In this case (if the player touches an enemy), it gets the category of the player (bodyA) which is 2, and it gets the category of the enemy (bodyB), which is 4. So 2 (0b10) OR 4 (0b100) is equal to 6 (0b110) which is assigned to collision.

  2. So then in the if statement, it checks if the collision of 6 is equal to (PC.player | PC.enemy), which is true, therefore an interaction occurred between the player and enemy since it would be if 6 == 6.

You can use the variable collision to test any interaction. For example in my test game I have the following function that tests what objects touched.

func didBeginContact(contact: SKPhysicsContact) {
    let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

    //Collision between the penguin and land
    if collision == PhysicsCategory.Land | PhysicsCategory.Animal {
        lostLevel()
    } else if collision == PhysicsCategory.Animal | PhysicsCategory.Pillow {
        animalCounter -= 1
        if animalCounter == 0 {
            wonLevel()
        }
    }
}
like image 37
Ada Avatar answered Oct 22 '22 13:10

Ada