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 categoryBitMask
s 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' categoryBitMask
s. 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 bowlingball
s 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 pool
s 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
- 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
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)
- 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
}
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
Checking If an Array of Custom Objects Contain a Specific Custom Object
How to Convert a String to a Cstring in the Swift Language
Parsing a Iso8601 String to Date in Swift
How to Limit Flatmap Concurrency in Combine Still Having All Source Events Processed
How to Make the Memberwise Initialiser Public, by Default, for Structs in Swift
Obj-C Cocoapods + Swift Framework
Macos Menubar Application: Main Menu Not Being Displayed
Is There an Alternative to Initialize() in MACos Now That Swift Has Deprecated It
Swift Dictionary: Remove Time Complexity
How to Have a Swift Script Use Multiple Files
How to Open Your App in Settings iOS 11
How to Execute Code Once and Only Once in Swift
Swift 4 Decode Simple Root Level JSON Value
Distinction Between Private and Fileprivate Top-Level Classes
iOS 10 Imessage App Extension: How to Calculate the Height of the Extra Tall Navbar
In Swift, How to Have a Uiscrollview Subclass That Has an Internal and External Delegate