hb22
hb22

Reputation: 383

SpriteKit - Making certain SpriteNodes NOT collide without disabling their physics bodies

I have 4 SpriteNodes on screen that I would like not to bounce off each other but rather flow through each other, while maintaining collision with certain SpriteNodes and the frame of the screen. I have already declared categories for all of them:

let BCategory  : UInt32 = 0x1 << 0
let B2Category : UInt32 = 0x1 << 1
let B3Category : UInt32 = 0x1 << 2
let B4Category : UInt32 = 0x1 << 3
let BotCategory : UInt32 = 0x1 << 4
let PadCategory : UInt32 = 0x1 << 5
let WallCategory : UInt32 = 0x1 << 6

And added Bitmasks as well:

    b.physicsBody!.categoryBitMask = BCategory
    b2.physicsBody!.categoryBitMask = B2Category
    b3.physicsBody!.categoryBitMask = B3Category
    b4.physicsBody!.categoryBitMask = B4Category

    p.physicsBody!.categoryBitMask = PadCategory
    wall.categoryBitMask = WallCategory
    bot.physicsBody?.categoryBitMask = BotCategory

    b.physicsBody?.contactTestBitMask = BotCategory | PadCategory
    b2.physicsBody?.contactTestBitMask = BotCategory | PadCategory
    b3.physicsBody?.contactTestBitMask = BotCategory | PadCategory
    b4.physicsBody?.contactTestBitMask = BotCategory | PadCategory

As well as checked for collisions between the SpriteNodes I would like to collide via:

func didBeginContact(contact: SKPhysicsContact) {

Is it possible to have all of the b's (b, b2, b3, b4) collide with p, wall, and bot but not amongst themselves? Any help is very much appreciated!

EDIT: The issue I'm having is that the SpriteNodes represented by the b's (b, b2, b3, b4) are bouncing off of each other. I would like each of the b's to only bounce off the SpriteNodes contained in BotCategory and PadCategory, and slide past/through the other b's.

Upvotes: 3

Views: 1040

Answers (2)

Steve Ives
Steve Ives

Reputation: 8134

You've gotten mixed up between collisions and contacts. A collision is when 2 physics bodies hit and bounce off each other. Collisions are handled completely by the game engine. You just have to set the necessary collisionBitMask

A contact is when the sprite-kit game engine tells you code that 2 physics bodies have made contact. You can then make one explode, update a score etc. 2 bodies can collide without you being notified of a contact, or they can make contact without a collision. You have to set the contactTestBitMask, set your program as the physicsWorld.contactDelegate and implement either or both of the didBeginContact/didEndContact functions.

Whether or not 2 bodies collide is controlled by the collisionBitMask. By default, this is set to UInt32.Max i.e. everything collides with everything. Given 2 bodies, A and B, it's possible for body A to collide with body B i.e. to bounce off it, but for body B NOT to collide with body A i.e to be unaffected by the collision.

Whether or not your code is notified about 2 bodies making contact is controlled by the contactTestBitMask. This is set to 0 by default i.e. no contacts are registered. Given 2 bodies A and B, you only have to set the contactTestBit mask for body A to the category for bodyB for all contacts between the 2 to be registered.

I find this function useful to check what collision and contacts you have in your scene:

   func checkPhysics() {

        // Create an array of all the nodes with physicsBodies
        var physicsNodes = [SKNode]()

        //Get all physics bodies
        enumerateChildNodesWithName("//.") { node, _ in
            if let _ = node.physicsBody {
                physicsNodes.append(node)
            } else {
                print("\(node.name) does not have a physics body so cannot collide or be involved in contacts.")
            }
        }

        //For each node, check it's category against every other node's collion and contctTest bit mask
        for node in physicsNodes {
            let category = node.physicsBody!.categoryBitMask
            // Identify the node by its category if the name is blank
            let name = node.name != nil ? node.name : "Category \(category)"

            if category == UInt32.max {print("Category for \(name) does not appear to be set correctly as \(category)")}

            let collisionMask = node.physicsBody!.collisionBitMask
            let contactMask = node.physicsBody!.contactTestBitMask

            // If all bits of the collisonmask set, just say it collides with everything.
            if collisionMask == UInt32.max {
                print("\(name) collides with everything")
            }

            for otherNode in physicsNodes {
                if (node != otherNode) && (node.physicsBody?.dynamic == true) {
                    let otherCategory = otherNode.physicsBody!.categoryBitMask
                    // Identify the node by its category if the name is blank
                    let otherName = otherNode.name != nil ? otherNode.name : "Category \(otherCategory)"

                    // If the collisonmask and category match, they will collide
                    if ((collisionMask & otherCategory) != 0) && (collisionMask != UInt32.max) {
                        print("\(name) collides with \(otherName)")
                    }
                    // If the contactMAsk and category match, they will contact
                    if (contactMask & otherCategory) != 0 {print("\(name) notifies when contacting \(otherName)")}
                }
            }
        }

Just call it after setting up the physics with checkPhysics() and it will list which bodies collide and for which contact you will be notified.

Upvotes: 5

T. Benjamin Larsen
T. Benjamin Larsen

Reputation: 6393

You need to specify the collisionBitMask on the physicsBodies as well as the category/contact-masks. This is by default turned ON for all bits. So something like this:

b.physicsBody?.collisionBitMask = BotCategory | PadCategory

Upvotes: 4

Related Questions