David Sundström David Sundström - 4 years ago 117
Swift Question

How to make two different collisions which makes different things happen?

I have made a game where the basics is, if a sprite node collides with a wall i looses and if it collides with a separate node(Ball2) you will you win. I have made how to detect the collision which makes you lose the game but not when the Ball collides with ball2. So my question is how to make two different collisions which makes different things happen?
I would really love some help, thanks

struct PhysicsCategogy {
static let Ball : UInt32 = 0x1 << 1
static let Ball2 : UInt32 = 0x1 << 1
static let WallH : UInt32 = 0x1 << 2
static let WallH2 : UInt32 = 0x1 << 3
}

class GameScene: SKScene, SKPhysicsContactDelegate {

var Ball = SKSpriteNode(imageNamed: "Ball")
var Ball2 = SKSpriteNode(imageNamed: "Ball")
var WallH = SKSpriteNode(imageNamed: "WallH")


override func didMoveToView(view: SKView) {

self.view?.paused = false

self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self

Ball.position = CGPointMake(self.frame.width / 2 - 450, self.frame.height / 2)
Ball.setScale(0.5)
Ball.physicsBody = SKPhysicsBody(rectangleOfSize: Ball.size)
Ball.physicsBody?.affectedByGravity = false
Ball.physicsBody?.dynamic = true
Ball.physicsBody?.contactTestBitMask = PhysicsCategogy.Ball
Ball.physicsBody?.collisionBitMask = 0
Ball.physicsBody?.collisionBitMask = 2
addChild(Ball)

Ball2.position = CGPointMake(self.frame.width / 2 + 450, self.frame.height / 2)
Ball2.setScale(0.5)
Ball2.physicsBody = SKPhysicsBody(rectangleOfSize: Ball.size)
Ball2.physicsBody?.affectedByGravity = false
Ball2.physicsBody?.dynamic = true
Ball2.physicsBody?.contactTestBitMask = PhysicsCategogy.Ball2
Ball2.physicsBody?.collisionBitMask = 1

addChild(Ball2)


WallH.position = CGPointMake(self.frame.width / 2, self.frame.height / 2 + 190)
WallH.setScale(7.5)
WallH.physicsBody = SKPhysicsBody(rectangleOfSize: WallH.size)
WallH.physicsBody?.affectedByGravity = false
WallH.physicsBody?.contactTestBitMask = PhysicsCategogy.WallH
WallH.physicsBody?.collisionBitMask = 0

self.addChild(WallH)

}

func didBeginContact(contact: SKPhysicsContact) {
self.backgroundColor = SKColor.whiteColor()
self.view?.paused = true

}
}

Answer Source

There are a lot of things in your code which are wrong...First, you have to clean your project from typos and from a redundant code, for example this makes no sense :

Ball.physicsBody?.collisionBitMask = 0  //Remove it
Ball.physicsBody?.collisionBitMask = 2

Now let's go from the beginning. You have wrongly set your physics categories. You want something like this:

struct PhysicsCategory {
        static let Ball     : UInt32 = 1 << 0         //1
        static let Ball2    : UInt32 = 1 << 1         //2
        static let WallH    : UInt32 = 1 << 2         //4
        static let WallH2   : UInt32 = 1 << 3         //8
    }

Next thing would be, how to properly set category, collision and contact bit masks. You don't want this:

Ball.physicsBody?.contactTestBitMask = PhysicsCategory.Ball

This make no sense because there is only one instance of a Ball on the screen. You want to detect contacts between Ball and a Ball2, like this:

Ball.physicsBody?.contactTestBitMask = PhysicsCategory.Ball2

So lets set everything correctly (note that you haven't set Ball's categoryBitMask at all):

Ball.physicsBody?.categoryBitMask = PhysicsCategory.Ball
Ball.physicsBody?.contactTestBitMask = PhysicsCategory.Ball2
Ball.physicsBody?.collisionBitMask = PhysicsCategory.Ball2

Here you set Ball's category to PhysicsCategory.Ball and you set its contact and collision bit mask to PhysicsCategory.Ball2. You are doing this because you want to detect contacts and to make collisions happen between these two bodies. Same goes for Ball2:

Ball2.physicsBody?.categoryBitMask = PhysicsCategogy.Ball2
Ball2.physicsBody?.contactTestBitMask = PhysicsCategogy.Ball
Ball2.physicsBody?.collisionBitMask = PhysicsCategogy.Ball

You can apply this same logic to the walls as well. For example, if you want to have collisions between balls and a wall, but you don't want to register contacts when collision happen (means, you don't want didBeginContact being invoked), you will do something like this (assuming that categoryBitMask is set properly for every body):

//You don't want to register contacts between Ball and a WallH
Ball.physicsBody?.contactTestBitMask = PhysicsCategory.Ball2

//But still, you want collisions with Ball2 and a WallH
Ball.physicsBody?.collisionBitMask = PhysicsCategory.Ball2 | PhysicsCategory.WallH

//You don't want to register contacts between Ball2 and a WallH
Ball2.physicsBody?.contactTestBitMask = PhysicsCategory.Ball

//But still, you want collisions with Ball and a WallH
Ball2.physicsBody?.collisionBitMask = PhysicsCategory.Ball | PhysicsCategory.WallH

WallH.physicsBody?.collisionBitMask = PhysicsCategory.Ball | PhysicsCategory.Ball2

Please make sure that you are understanding the difference between terms contact and collision. Contact may happen even if collisions are disabled (collisionBitMask = 0), and vice versa. This is dependable on how you have set your contact/collision bit masks. You can choose in SpriteKit what part of physics engine you want to use. Contact detection, physics simulation or both. If you are not interested in contact detection, you should not set contact mask because of performance reasons.

Finally, to handle contacts, you can use something like this:

func didBeginContact(contact: SKPhysicsContact) {

var firstBody, secondBody: SKPhysicsBody

if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {

   firstBody = contact.bodyA
   secondBody = contact.bodyB
} else {

    firstBody = contact.bodyB
    secondBody = contact.bodyA
}


if ((firstBody.categoryBitMask & PhysicsCategory.Ball) != 0 &&
   (secondBody.categoryBitMask & PhysicsCategory.Ball2 != 0)) {

    //firstBody is a Ball
    //secondBody is a Ball2

    print ("Detected contact between Ball and a Ball2")

   }

}

If you want to know a little bit more about some parts of your code like what does self.physicsWorld.contactDelegate = self or how didBeginContact is invoked, take a look at this. For a bit different implementation of what I've just wrote for you, but a great example of how to accomplish the same thing, take a look at this.

But at the end, an official docs are always a good place to start, or to come back if you are unsure about anything.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download