Skphysicsbody Avoid Collision Swift/Spritekit

SKPhysicsBody avoid collision Swift/SpriteKit

The bitmask is on 32 bits. Declaring them like you did corresponds to :

enum CollisionType:UInt32{
case Bird = 1 // 00000000000000000000000000000001
case Coin = 2 // 00000000000000000000000000000010
case Border = 3 // 00000000000000000000000000000011
}

What you want to do is to set your border value to 4. In order to have the following bitmask instead :

enum CollisionType:UInt32{
case Bird = 1 // 00000000000000000000000000000001
case Coin = 2 // 00000000000000000000000000000010
case Border = 4 // 00000000000000000000000000000100
}

Keep in mind that you'll have to follow the same for next bitmask : 8, 16, ... an so on.

Edit :

Also, you might want to use a struct instead of an enum and use another syntax to get it easier (it's not mandatory, just a matter of preference) :

struct PhysicsCategory {
static let None : UInt32 = 0
static let All : UInt32 = UInt32.max
static let Bird : UInt32 = 0b1 // 1
static let Coin : UInt32 = 0b10 // 2
static let Border : UInt32 = 0b100 // 4
}

That you could use like this :

bird.physicsBody!.categoryBitMask = PhysicsCategory.Bird
bird.physicsBody!.collisionBitMask = PhysicsCategory.Border

coin.physicsBody!.categoryBitMask = PhysicsCategory.Coin
coin.physicsBody!.collisionBitMask = PhysicsCategory.Border

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

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

Avoiding collision in spriteKit on iOS while maintaining contact

You might have a different problem in your code elsewhere, because this is working for me. The bodies do not collide and the contact happens.

Sample playground code:

import PlaygroundSupport
import SpriteKit

class GameScene: SKScene, SKPhysicsContactDelegate {

var stoneNode: SKShapeNode!
var birdNode: SKShapeNode!

override func didMove(to view: SKView) {

let stoneCategory : UInt32 = 0x1 << 1
let birdCategory : UInt32 = 0x1 << 2

physicsWorld.contactDelegate = self

stoneNode = SKShapeNode(circleOfRadius: 30)
stoneNode.name = "stone"
stoneNode.position = CGPoint(x: 0, y: 100)
addChild(stoneNode)
stoneNode.physicsBody = SKPhysicsBody(circleOfRadius: 30)
stoneNode.physicsBody?.categoryBitMask = stoneCategory
stoneNode.physicsBody?.contactTestBitMask = birdCategory
stoneNode.physicsBody?.collisionBitMask = 0

birdNode = SKShapeNode(circleOfRadius: 40)
birdNode.name = "bird"
birdNode.position = CGPoint(x: 0, y: -100)
addChild(birdNode)
birdNode.physicsBody = SKPhysicsBody(circleOfRadius: 40);
birdNode.physicsBody?.categoryBitMask = birdCategory
birdNode.physicsBody?.contactTestBitMask = stoneCategory
birdNode.physicsBody?.collisionBitMask = 0
birdNode.physicsBody?.isDynamic = false;
}

func didBegin(_ contact: SKPhysicsContact) {
print("Contact between "+contact.bodyA.node!.name!+" and "+contact.bodyB.node!.name!); // --> Never called
}
}

// Load the SKScene from 'GameScene.sks'
let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 640, height: 480))
if let scene = GameScene(fileNamed: "GameScene") {
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill

// Present the scene
sceneView.presentScene(scene)
}

PlaygroundSupport.PlaygroundPage.current.liveView = sceneView

And in the console, when the first body drops, being affected by gravity, it's printed:

Contact between stone and bird

How to Detect collision in Swift, Sprite kit

  1. Define unique categories, ensure your class is a SKPhysicsContactDelegate and make yourself the physics contact delegate:

//Physics categories

let enemyCategory:    UInt32 = 1 << 1
let bulletCategory: UInt32 = 1 << 2

class GameScene: SKScene, SKPhysicsContactDelegate {
physicsWorld.contactDelegate = self

  1. Assign the categories (usually in didMove(to view:) :

    enemy.physicsBody.catgeoryBitMask = enemyCategory
    bullet.physicsBody.catgeoryBitMask = bulletCategory

(Make sure you've created physics bodies for each node)


  1. Set up collisions:

enemy.physicsBody?.collisionBitMask = 0 // enemy collides with nothing
bullet.physicsBody?.collisionBitMask = 0 // bullet collides with nothing

or even:

for node in [enemy, bullet] {
node.physicsBody?.collisionBitMask = 0 // collides with nothing
}

  1. Set up contacts

    bullet.physicsBody?.collisionBitMask = enemyCategory // bullet contacts enemy

Make sure that at least one of the objects involved in each potential contact has the isDynamic property on its physics body set to true, or no contact will be generated. It is not necessary for both of the objects to be dynamic.

You should now get didBegin called when the bullet and the enemy make contact. You could code didBegin like this:

  func didBegin(_ contact: SKPhysicsContact) {
print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")

let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

switch contactMask {
case bulletCategory | enemyCategory:
print("bullet and enemy have contacted.")
let bulletNode = contact.bodyA.categoryBitMask == bulletCategory ? contact.bodyA.node : contact.bodyB.node
enemyHealth -= 10
bulletNode.removeFromParent
default:
print("Some other contact occurred")
}

}

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;
}

collision not detected between SKSpitekit nodes

First in your didMoveTo or sceneDidLoad, you need to set the physicsContactDelegate:

override func sceneDidLoad () {
physicsWorld.contactDelegate = self
buildMaze()
addDot()
}

To set the contact/collision mask, you have to do it this way, because they're based on bitwise operation:

Let's suppose you want collision between dot and walls

struct PhysicsCategory {
static let wall: UInt32 = 0x1 << 1
static let dot: UInt32 = 0x1 << 2
}

You can put the struct above you class if you want

Then, when you assign physics body, you have to set the bitmask:

For dots:

    dot.physicsBody?.categoryBitMask = PhysicsCategory.dot
dot.physicsBody?.contactTestBitMask = PhysicsCategory.wall
dot.physicsBody?.collisionBitMask = PhysicsCategory.wall

//Collision is different from contact, so if you want to avoid collision
//dot.physicsBody?.collisionBitMask = PhysicsCategory.dot

Collision is different from contact, check apple documentation about it

For walls:

    northWall.physicsBody?.categoryBitMask = PhysicsCategory.wall
northWall.physicsBody?.contactTestBitMask = PhysicsCategory.dot
northWall.physicsBody?.collisionBitMask = PhysicsCategory.dot

//Do the same for all walls

If you want walls to contact or collide with more than one object:

    northWall.physicsBody?.contactTestBitMask = PhysicsCategory.dot | PhysicsCategory.other
northWall.physicsBody?.collisionBitMask = PhysicsCategory.dot | PhysicsCategory.other

For walls are valid all consideration as per dots



Related Topics



Leave a reply



Submit