SKPhysicsBody Collision Not Working
Asked Answered
B

2

6

Below is my "Floor.swift" class below which is basically a bunch of walls. I have objects coming from the top of the screen and once the Floor and SKSpriteNodes collide, I'd like the SKSpriteNode to be removed. Below is my Floor class.

import Foundation
import SpriteKit

class Floor: SKNode {
    override init() {
        super.init()

        let leftWall = SKSpriteNode(color: UIColor.clear, size: CGSize(width: 5, height: 50))
        leftWall.position = CGPoint(x: 0, y: 50)
        leftWall.physicsBody = SKPhysicsBody(rectangleOf: leftWall.size)
        leftWall.physicsBody!.isDynamic = false
        self.addChild(leftWall)

        let rightWall = SKSpriteNode(color: UIColor.clear, size: CGSize(width: 5, height: 50))
        rightWall.position = CGPoint(x: 375, y: 50)
        rightWall.physicsBody = SKPhysicsBody(rectangleOf: rightWall.size)
        rightWall.physicsBody!.isDynamic = false
        self.addChild(rightWall)

        let bottomWall = SKSpriteNode(color: UIColor.clear, size: CGSize(width: 500, height: 10))
        bottomWall.position = CGPoint(x: 150, y: -5)
        bottomWall.physicsBody = SKPhysicsBody(rectangleOf: bottomWall.size)
        bottomWall.physicsBody!.isDynamic = false
        self.addChild(bottomWall)

        self.physicsBody = SKPhysicsBody(bodies: [leftWall.physicsBody!, rightWall.physicsBody!, bottomWall.physicsBody!])

        self.physicsBody?.categoryBitMask = floorCategory
        self.physicsBody?.contactTestBitMask = nailDropCategory | pointCategory | lifeCategory
        self.physicsBody?.collisionBitMask = balloonCategory
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemted")
    }
}

GameScene class under "func didBegin(_ contact: SKPhysicsContact)" I wrote:

func didBegin(_ contact: SKPhysicsContact) {        
        if (contact.bodyA.categoryBitMask == nailDropCategory | pointCategory | lifeCategory) && (contact.bodyB.categoryBitMask == floorCategory) {
           // contact.bodyB.node!.removeFromParent()
            print("COLLISION")
        }
}

As you can see, in my Floor class I set the object as "self.physicsBody = SKPhysicsBody(bodies:)" with all my SKSpriteNode. But for some reason I'm not getting any detection what's so ever. I made my "Floor" class be a "contactTestBitMask" on each class of my objectCategory, pointCategory, lifeCategory. This has been an issue I've been stuck on for a minute now, any ideas?

Update:

So I'm using this code below now and it's working! Only issue is, the default case is removing my "balloonCategory" also and I don't want it to do that. How can I make it ignore only that category but also act as a wall to the balloon?

func didBegin(_ contact: SKPhysicsContact) {
        let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

        switch contactMask {
        case balloonCategory | nailDropCategory:
            print("nailDrop and balloon have contacted.")
            contact.bodyB.node!.removeFromParent()
            self.run(SKAction.playSoundFileNamed("lostlifesound.mp3", waitForCompletion:false))
            lifeLost -= 1
        case balloonCategory | pointCategory:
            print("point and balloon have contacted.")
            contact.bodyB.node!.removeFromParent()
            totalPoints += 2
            self.run(SKAction.playSoundFileNamed("pointsound.mp3", waitForCompletion:false))
        case balloonCategory | lifeCategory:
            print("life and balloon have contacted.")
            contact.bodyB.node!.removeFromParent()
            lifeLost += 1
            self.run(SKAction.playSoundFileNamed("pointsound.mp3", waitForCompletion:false))
        default:            
            contact.bodyB.node!.removeFromParent()
            print("Removed \(String(describing: contact.bodyB.node!.name))")
        }
    }
Brenna answered 26/12, 2017 at 23:26 Comment(6)
nailDropCategory? pointCategory? lifeCategory? Nobody but you knows how you define your bit masks.Depressive
Well, I defined it on all my other classes and all the other collisions works so I’m just lost lolBrenna
If physics is already working in your project then seems like this should work. Are you able to reproduce this issue in a sample project & share on Github? Seems like the solution may come from debugging.Kev
@Kev Take a look at my code update. I'm using the switch method now. In the default: section, it's removing all other nodes which is perfect but I don't want it to remove my balloonCategory but I also don't want it to remove the balloonCategory when it collides with my floorCategory. I just want it to act as a wall to it. How can I do that?Brenna
@Dewan. You can't always remove the nodeB object, because you don't know which object is nodeA and which is nodeB, but there is an easy way to resolve this. Can you tell me which node(s) do you want removed for each contact?Hyalite
@Brenna When you say you want the default case to act as a wall to the ballon, what exactly do you mean? If you just want to wall to stop the ballon, but for nothing else to happen when they touch, then you need to make sure that collision detection is set up correctly and in didBegin(contact:) you don't have to do anything.Hyalite
K
5

It sounds like you would benefit from assigning names to all your object types, like so:

class Balloon: SKSpriteNode {
    init() {
        super.init(texture: nil, color: .blue, size: CGSize(width: 50, height: 50))
        self.name = "balloon"
    }
}

Now when you make your check in the switch, you can verify the identity of objects before deciding to delete them. See the change I made to the default case in your switch statement, below. This way, the node will not be removed if it is a balloon. The physics collisions will continue to work.

func didBegin(_ contact: SKPhysicsContact) {
    let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

    switch contactMask {
    case balloonCategory | nailDropCategory:
        print("nailDrop and balloon have contacted.")
        contact.bodyB.node!.removeFromParent()
        self.run(SKAction.playSoundFileNamed("lostlifesound.mp3", waitForCompletion:false))
        lifeLost -= 1
    case balloonCategory | pointCategory:
        print("point and balloon have contacted.")
        contact.bodyB.node!.removeFromParent()
        totalPoints += 2
        self.run(SKAction.playSoundFileNamed("pointsound.mp3", waitForCompletion:false))
    case balloonCategory | lifeCategory:
        print("life and balloon have contacted.")
        contact.bodyB.node!.removeFromParent()
        lifeLost += 1
        self.run(SKAction.playSoundFileNamed("pointsound.mp3", waitForCompletion:false))
    default:
        if contact.bodyB.node!.name == "balloon" {
            print("Balloon collided but we will not remove it.")
            return
        } else {
            contact.bodyB.node!.removeFromParent()
            print("Removed \(String(describing: contact.bodyB.node!.name))")
    }
}
Kev answered 30/12, 2017 at 21:5 Comment(6)
Dude I truly appreciate your help so much! You're a blessing brotha and I hope you have a wonderful new year! Thank you so much once againBrenna
Same to you, and best of luck with your game!Kev
Thanks! Last question, what's your personal favorite ad network for iOS?Brenna
Sorry, I couldn't answer that as I've never used them. I'm a believer in design that lends itself to targeted & direct monetization (paid apps, in-app purchases, or the freemium model). That said, if you can make a game that is super popular among casual players you could probably pick any of the top ad networks and make a buck. Just Google top ad network for ios and see what you get :)Kev
You're a good dude foreal, wish you many blessings man. Gonna do that right now, have a good one!!Brenna
@Brenna - if you don't want anything to happen when balloon touches wall, then just turn off contact detection for between these objects, rather than having code in didBegin to ignore the contact. Your game will run faster and have less code in it.Hyalite
H
1

You can't guarantee that nodeB is the floor and is nodeA really nailDropCategory | pointCategory | lifeCategory?

I'm assuming that naildDrop, point and life are 3 objects that you want to be notified for when any one touches the floor, so try structuring your didBegin(contact:) 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 nailDropCategory | FloorCategory:
        print("nailDrop and floor have contacted.")
     case pointCategory | FloorCategory:
        print("point and floor have contacted.")
     case lifeCategory | FloorCategory:
        print("life and floor have contacted.")
     default:
        print("Undetected collision occurred")
     }
 }

Have you actually created an object from your Floor class?

Hyalite answered 27/12, 2017 at 10:46 Comment(5)
It says, "didBeginContact entered for nil and nil" and returning the "default:" in the switch. Also, doing the line "self.physicsBody = SKPhysicsBody(bodies: [leftWall.physicsBody!, rightWall.physicsBody!, bottomWall.physicsBody!])" overlaps my existing physicsBody and causes some issues. Not too sure what's going on because this worked before so I'm completely lostBrenna
So we know that something has contacted - make sure that every node in your scene has a name, then you’ll know what has collided. I’m not sure what you mean when you say that self,physicsBody... overlaps the existing physics body, because according to your code, the Floor object doesn’t have an existing physics body.Hyalite
The SKPhysicsBody array with all the bodies along with this line of code " bottomWall.physicsBody = SKPhysicsBody(rectangleOf: bottomWall.size)" which I also did for leftWall and rightWall. I'm literally creating double Physics Body and they're overlapping so at this point I'm not sure what's going on. This is stressfulBrenna
Take a look at my code update. If you figure it out for me then I'll mark yours an the answer to my question since you figured this out for meBrenna
@Brenna Looks like you've got the answer, but don't have code that ignores contacts (e.g. your balloon and wall), just have those contacts never occur. i.e. if you don't want to do anything when the balloon touches the wall, don't have their contactTestBitMasks set up so that didBegin is not called when they touch.Hyalite

© 2022 - 2024 — McMap. All rights reserved.