Can't understand how collision bit mask works - swift

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.

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.

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.
}
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.
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()
}
}
}

Related

How to use correctly Scenekit's category mask, collision mask and physicsbody, contactTestBitMask and make proper collisions?

I have been trying to learn scenekit and have finished one book but only collision detection part is not understood, maybe the most important part.
There is category mask, collusion mask and physicsbody?.contactTestBitMask.
I want to create a simple game to get this but I got stuck. I am not sure I get the idea.
In game the game, there is a ball and collects pearls, and stays away from rocks and holes. In this case:
Category masks:
ball = 0b0001
pearls = 0b0010
rocks = 0b0100
holes = 0b1000
physicsBody?.contactTestBitMask:
ball = pearl || rocks // means 0b1110
pearls = 1
rocks = 1
Collusion masks are 1 because they all collide with each other.
I am not sure I get this collision issue. So before I begin to write code, I wanted to be sure. In SCNPhysicsContactDelegate, function below solves how to learn when they contact with each other:
physicsWorld(_ didBegin contact: _) {
var contactNode:SCNNode!
if contact.nodeA.name == "ball" {
contactNode = contact.nodeB
} else {
contactNode = contact.nodeA
}
if contactNode.physicsBody?.categoryBitMask == 0b0010 {
// mean pearls
// raise score etc
}
if contactNode.physicsBody?.categoryBitMask == 0b0100 || 0b1000{
if contactNode.name == "Rock" { print("You rocked") }
if contactNode.name == "Hole" { print("You need to climb") }
}
}
I have searched youtube and stack but there is only one explanation.
Stack Link
Youtube videos are not explaining these.
The book examples are copyrighted, so I can't put them on here.
Thank you,
Have a nice day.
You are not using bitwise operators. https://docs.swift.org/swift-book/LanguageGuide/AdvancedOperators.html
change
if contactNode.physicsBody?.categoryBitMask == 0b0100 || 0b1000
to
if contactNode.physicsBody?.categoryBitMask == 0b0100 | 0b1000
The single | and & are the correct operators to use for bitwise operations such as these.
There are 3 function to set when you do collision:
mBallBody->setCategoryBitmask(CATEGORY_BALL);
mBallBody->setContactTestBitmask(CATEGORY_WALL_LEFT_RIGHT | CATEGORY_WALL_TOP | CATEGORY_BOX | CATEGORY_LAUNCHER);
mBallBody->setCollisionBitmask(CATEGORY_WALL_LEFT_RIGHT | CATEGORY_WALL_TOP | CATEGORY_BOX | CATEGORY_LAUNCHER);
setCategoryBitmask() --> to set what category this physics belong to!
setContactTestBitmask() --> a mask that defines which categories of bodies cause intersection notifications with this physics body
setCollisionBitmask() --> a mask that defines which categories of phyics bodies can collide with this physics body
so let say ball and wall, ball can collide with wall, so you set contact test bitmask to ball | wall, using | is the bitmask operation.
So you need to set collision category bitmask 1 2 4 8 16 32 and so on.

collisionBitMask doesn't work

I wrote the following code to detect collisions between the player, a wall and a npc:
struct PhysicsCategories{
static let player: UInt32 = 0x1 << 1
static let npc: UInt32 = 0x1 << 2
static let wall: UInt32 = 0x1 << 3
static let all: UInt32 = 0xFFFFFFFF
}
these are the setting for the player:
self.physicsBody!.categoryBitMask = Constants.PhysicsCategories.player
self.physicsBody!.collisionBitMask = Constants.PhysicsCategories.npc | Constants.PhysicsCategories.wall
self.physicsBody!.contactTestBitMask = Constants.PhysicsCategories.npc
these are the settings for the wall:
tileNode.physicsBody!.categoryBitMask = Constants.PhysicsCategories.wall
tileNode.physicsBody!.collisionBitMask = Constants.PhysicsCategories.player | Constants.PhysicsCategories.npc
these are the settings for the npc:
npc.physicsBody!.categoryBitMask = Constants.PhysicsCategories.npc
npc.physicsBody!.collisionBitMask = Constants.PhysicsCategories.wall | Constants.PhysicsCategories.player | Constants.PhysicsCategories.npc
npc.physicsBody!.contactTestBitMask = Constants.PhysicsCategories.player
I want the npc to collide with the wall and the player. The player should collide with the npc and the wall.
The result is that the player can collide with the wall and the npc, but the npc can only collide with the player and goes through the walls..
Has anyone an idea what i'm doing wrong here? The npc is walking randomly around the scene, is the collisionbitmask behavior affected by this?
EDIT: If I set the dynamic property of the npc to true, it works. But I don't want the npc to be dynamic because I don't want that the player can push the npc away.
Why is it only working when the npc is dynamic?
If you don't want the player to be able to push the npc around, don't mess around with the dynamic property, just switch off collisions between the npc and the player:
npc.physicsBody!.collisionBitMask &= ~Constants.PhysicsCategories.player
This will set the bit representing the player's category to 0 in npc's collisionBitMask without affecting any of npc's other collision settings.
or don't set them in the first place. Change:
npc.physicsBody!.collisionBitMask = Constants.PhysicsCategories.wall | Constants.PhysicsCategories.player | Constants.PhysicsCategories.npc
to
npc.physicsBody!.collisionBitMask = Constants.PhysicsCategories.wall | Constants.PhysicsCategories.npc
So npc no longer collides with player - this means that if the 2 come into contact, npc will be unaffected by player and carry on moving completely unaffected by the collision. The contactTestbitMask is still set however, so your code will be notified of the collision.
The player, however, will bounce off the npc unless you also do the same for the player:
player.physicsBody!.collisionBitMask &= ~Constants.PhysicsCategories.npc
When you say :
The npc is walking randomly around the scene, is the collisionbitmask
behavior affected by this?
Do you mean it's just bouncing off things and bouncing around the screen, or is there a problem with your movement code? If the former, then do as Kod said and set restitution to 0. From https://developer.apple.com/documentation/spritekit/skphysicsbody :
The restitution property determines how much energy a body maintains
during a collision—its bounciness.
If I understood correctly, you want the player and the NPC to "bounce off the walls", but not bounce off each other. The solution would be to not set their collisionBitMasks to each other‘s categoryBitMasks, but only their contactTestBitMasks.
As to why the NPC needs to be dynamic to bounce off the walls: One participant must be dynamic for a collision to show effect: the one that bounces off.

Stop objects from colliding using SpriteKit

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.
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
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.

Why are methods sometimes called more than once in collision checking?

Like many game programmers in SpriteKit, I use collision detection between certain objects to call methods or set properties. However, sometimes my actions happen twice in a collision. Here is an example from inside my didBeginContact method:
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (WWPhysicsCategoryShip | WWPhysicsCategoryWeapon)) {
SKNode *weapon = (contact.bodyA.categoryBitMask == WWPhysicsCategoryWeapon) ? contact.bodyA.node : contact.bodyB.node;
[weapon removeFromParent];
_localPlayerHP = _localPlayerHP - 5;
NSLog(#"My Health is now at %i.", _localPlayerHP);
}
You'll notice that my ship's health is decreased by 5 and the enemy weapon that contacted the ship is removed. But when testing the game, I find that my ship's health is often reduced by 10, in increments of 5 (indicating that my decrease health method has fired more than once). I find this odd since the weapon is removed on first contact. How can I ensure that my health is only decreased by 5 once per contact?
Maybe you can try this
if weapon.parent != nil {
_localPlayerHP = _localPlayerHP - 5;
}
This question talks about another way by making the ship invulnerable for a few seconds after it gets hit.
didBeginContact is being called multiple times for the same SKPhysicsBody

Sprite Kit Multiple Collisions

I'm going through a tutorial/class making a game in Sprite Kit and I'm having issues with multiple collisions happening at the same time.
I'm implementing the didBeginContact: method for all collisions which make the ball bounce because there seems to be a known issue where objects may "stick" to a body if the velocity is too low and angle is too narrow.
By making
_ball.physicsBody.collisionBitMask = 0;
and putting the below line in the didBeginContact
if(other body == firstbody && _ball == secondbody)
secondBody.velocity = (CGVectorMake(secondBody.velocity.dx * -1.0, secondBody.velocity.dy));
(or dy * -1.0 for vertical collisions)
I can make the object bounce naturally and it works.
The issue I'm having is when there multiple contacts are called on the _ball. If 1 collision makes the _ball reverse its direction and it hits another object of the same type which reverses direction again, the _ball will continue moving in it's original direction (Double Negative). The _ball just moves through these objects. I can make it bounce back if I
secondBody.categoryBitMask = 0;
but i then i have the issue of returning the _ball to it's original category.
Does anyone know if you an cycle through contacts or stop contacts on a body after it processes a contact once?
Any thoughts?
I had many problems with correct amount of collisions. Sometimes it would get one, sometimes none. So I tried this and it works. The only thing to change is in didBeginContact method.
I will presume that you declared categories like that you correctly implemented physics delegate. So:
//define collision categories on the top of implementation file
static const uint32_t category1 = 0x1 << 0;
static const uint32_t category2 = 0x1 << 1;
static const uint32_t category3 = 0x1 << 2;
Try to replace your code in didBeginContact with this one. I remember that correct collisions finally got to work after I did this.
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKNode *newFirstBody = contact.bodyA.node;
SKNode *newSecondBody = contact.bodyB.node;
uint32_t collision = newFirstBody.physicsBody.categoryBitMask | newSecondBody.physicsBody.categoryBitMask;
if (collision == (category1 | category2))
{
NSLog(#"hit");
}
}
Hope it helps