4
votes

Sometimes in my SpriteKit programs, my collisions and contacts (using SKPhysicsBody) don't trigger or work as expected. I think I have set up everything I need, but I am still not getting the right interactions.

Is there some code I can write that will check what will collide with what and what bodies are set up to generate contacts?

1
These kind of questions are actually appropriate for Code ReviewWhirlwind
This is only suited for Code Review if the code is working as expected and if the OP wants a review... From the sound of this question he doesn't want a review, he wants an error check of his code...Dan
Actually I don't really want my code reviewed as OI think it works, but I was trying to post something that would help people with collision/contact issues. Perhaps I should have posted a question stating that I was having issues and then my checkPhysics function as an answer?Steve Ives
Stack Overflow is a Q&A site, and there is no question here. I suppose you could post the code as an answer to your own question, with the question being "how can I diagnose SpriteKit collision-detection errors?"200_success
unless you use filter on your physics node array, this can get real slow real fast, and actually hurt you during testing, low FPS means that frame skipping could cause objects to go through one another, not firing off a did contactKnight0fDragon

1 Answers

7
votes

To help diagnose these types of problems, I have written a function that can be called from anywhere and which will analyse the current scene and produce a list of what nodes with collide with which others and which collisions my scene will be notified for.

The function is stand-alone and does not need to be told anything about the nodes in the scene.

The function is as follows:

            //MARK: - Analyse the collision/contact set up.
    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)"
            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)")}
                }
            }
        }  
    }

You also need to check these 3 things:

  1. the scene must be a SKPhysicsContactDelegate
  2. You must set physicsWorld.contactDelegate = self
  3. You need to implement one of the optional methods of SKPhysicsContactDelegate:

didBeginContact

didEndcontact

The function should be called once you have set up all of your pics - usually at the end of didMoveToView works:

checkPhysics()

When I call this function from the end of my didMoveToView in my practice Swift project, I get the following output:

Optional("shape_blueSquare") collides with Optional("Category 2147483648") Optional("shape_blueSquare") collides with Optional("shape_redCircle") Optional("shape_blueSquare") collides with Optional("shape_purpleSquare") Optional("shape_blueSquare") collides with Optional("shape_yellowTriangle") Optional("shape_redCircle") collides with Optional("Category 2147483648") Optional("shape_redCircle") collides with Optional("shape_blueSquare") Optional("shape_redCircle") notifies when contacting Optional("shape_purpleSquare") Optional("shape_redCircle") collides with Optional("shape_yellowTriangle") Optional("shape_redCircle") notifies when contacting Optional("shape_yellowTriangle") Optional("shape_purpleSquare") collides with Optional("Category 2147483648") Optional("shape_purpleSquare") collides with Optional("shape_yellowTriangle") Optional("shape_yellowTriangle") collides with everything didBeginContact entered for Optional("shape_purpleSquare") and Optional("shape_redCircle") didBeginContact entered for Optional("shape_purpleSquare") and Optional("shape_redCircle") didBeginContact entered for Optional("shape_yellowTriangle") and Optional("shape_redCircle")

Category 2147483648 is my edge boundary and it does not have a name. I gave it this category to match its collisionBitMask

Please feel free to try this function and let me know if it's useful or if there are any situations it does not handle.