4
\$\begingroup\$

I understand how the below DidBeginConact function works, but how do you detect which categories make contact when you have multiple colored bars (in the example code) or a friend category and enemy category when an enemy overlaps a friend or two enemies overlap and a projectile category intersects both at the same time?

func didBeginContact(contact: SKPhysicsContact) {

var firstBody: SKPhysicsBody
var 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 == RedBallCategory && 
   secondBody.categoryBitMask == GreenBarCategory {

    (secondBody.node!.removeFromParent())
    score++
    scoreLabel.text = "\(score)"
} else if firstBody.categoryBitMask == GreenBallCategory && 
          secondBody.categoryBitMask == RedBarCategory {

    (secondBody.node!.removeFromParent())
    score++
    scoreLabel.text = "\(score)"
}

I've tried so many different combinations I can't keep them all straight or remember what I've tried and what I haven't.

Thanks in advance for help and suggestions in this.

\$\endgroup\$
3
\$\begingroup\$

I'm not really a fan of this :

if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
    firstBody = contact.bodyA
    secondBody = contact.bodyB
} else {
    firstBody = contact.bodyB
    secondBody = contact.bodyA
}

and prefer to do the following:

        let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

        switch contactMask {
        // Redball and GreenBar have contacted
        case RedBallCategory | GreenBarCategory :
           score += 1       // ++ is deprecated
           scoreLabel.text = "\(score)"

           // Remove GreenBar
           let GreenBar = contact.bodyA.categoryBitMask == GreenBarCategory ? contact.bodyA.node! : contact.bodyB.node!
           GreenBar.removeFromParent

        // Greenball and RedBar have contacted
        case GreenBallCategory | RedBarCategory :
           score += 1       // ++ is deprecated
           scoreLabel.text = "\(score)"

           // Remove RedBar
           let RedBar = contact.bodyA.categoryBitMask == RedBarCategory ? contact.bodyA.node! : contact.bodyB.node!
           RedBar.removeFromParent

       default :
         //Some other contact has occurred
       }

You can just add as many RedBallCategory | GreenBarCategory cases as you need for all the contacts that you have to take action for in your game. Code each potential contact individually and you won't loose yourself in if...then...else

\$\endgroup\$
  • \$\begingroup\$ If you are worried about situations where multiple sprites are overlapping (contacting) at once, just trust SpriteKit to call didBeginContact once for every pair of contacts. So I you have three bodies overlapping (RedBar, GreenBall and RedBall), you'll get called 3 times for the RedBar/GreenBall contact, RedBar/RedBall and GreenBall/RedBall, if such contacts are possible and you have set up your contactTestBitMasks this way. \$\endgroup\$ – Steve Ives Apr 14 '16 at 12:06
  • \$\begingroup\$ Check out this answer stackoverflow.com/a/41160767/1430420 for some more things I've learnt about 3 or more objects colliding at once and how to handle it. \$\endgroup\$ – Steve Ives Dec 15 '16 at 23:03
1
\$\begingroup\$

Here is what worked best for my latest project based on Steve's answer, please note though this is using c++ and the cocos2dx framework:

PhysicsBody *a = contact.getShapeA()->getBody();
PhysicsBody *b = contact.getShapeB()->getBody();

int contactMask = a->getCategoryBitmask() | b->getCategoryBitmask();
switch (contactMask)
{
case PhysicsMasks::kInvaderCategory | PhysicsMasks::kShipFiredBulletCategory:
{
    Invader* invader = dynamic_cast<Invader*>(a->getNode()) ?
        dynamic_cast<Invader*>(a->getNode()) : dynamic_cast<Invader*>(b->getNode());

    Bullet* bullet = dynamic_cast<Bullet*>(a->getNode()) ?
        dynamic_cast<Bullet*>(a->getNode()) : dynamic_cast<Bullet*>(b->getNode());

    if (bullet && invader) {
        invaderDied(invader, bullet);
    }
}
break;
default:
    break;
}

Where the PhysicsMask are declared like this:

struct PhysicsMasks {
    static const int kInvaderCategory = 0x0001;
    static const int kShipFiredBulletCategory = 0x0002;
};

const PhysicsMasks physicsMasks{};
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.