1

Trying to make a stationary turret fire at a moving enemy (tower defense game).

static const uint32_t towerCategory  = 0x1 << 0;
static const uint32_t creepCategory  = 0x1 << 1;

If the above is right, the towerCategory should have the lower category number correct?

tower.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:80];
tower.physicsBody.dynamic = YES;

tower.physicsBody.categoryBitMask = towerCategory;
tower.physicsBody.contactTestBitMask = creepCategory;
tower.physicsBody.collisionBitMask = 0;

This should set the tower node to be in the towerCategory and react to contact between the creep moving into range (range being the 80 radius physics body). Since I also don't want the tower or creep to push each other, I set the collision to 0.

baddie.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:baddie.size];
baddie.physicsBody.dynamic = YES;

baddie.physicsBody.categoryBitMask = creepCategory;
baddie.physicsBody.contactTestBitMask = towerCategory;
baddie.physicsBody.collisionBitMask = 0;

Same thing for the enemy. I switch the category and contact bitMask though since this one should be a creep, and it contacts the tower. Still no collision though so set it to 0.

    SKPhysicsBody* firstBody;
    SKPhysicsBody* secondBody;

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

    if ((firstBody.categoryBitMask & towerCategory) != 0 &&
        (secondBody.categoryBitMask & creepCategory) != 0)
    {
        [secondBody.node removeAllActions];
    }

I am pretty sure the didBeginContact function is where things are failing. Since towerCategory is lower than creepCategory, it should always be placed in firstBody if my code is correct. Then when the creep moves into contact range it should remove all actions from it and stop on the tracks. However he just keeps moving through. Can someone point out what I am missing?

Klutch
  • 171
  • 1
  • 2
  • 13
  • If I change the collisionBitMap of either node to be the other, they do collide and end up pushing each other around the scene. But without it, the nodes pass right by each other as if didBeginContact is never called. – Klutch Apr 15 '14 at 20:41
  • I actually solved my own problem here by figuring out the bitMask for the tower was not actually lower. – Klutch Apr 15 '14 at 21:46
  • If your collisions & contacts are not working as expected, try adding my checkPhysics() function to your program and calling it after you have sett all of the categories and bitmasks - at the end of didMovetoview is usually a good place. See the code for it here: http://stackoverflow.com/questions/36570859/ios-spritekit-collisions-and-contacts-not-working-as-expected/36574240#36574240 – Steve Ives Apr 19 '16 at 09:59
  • Remember that a **collision** results in bodies pushing/bouncing off each other and you don't have to write any code for this - the physics engine does all the work. A **contact** is when 2 bodies touch (which may or may not result in a collision) and is handled completely by your code. – Steve Ives Apr 19 '16 at 10:14
  • Also, it's possible for bodyA to collide with bodyB but for bodyB not to collide with bodyA - in this case, when the 2 collide, bodyB will be totally unaffected and be unmoved by the collision but bodyA will be deflected in some way. A **contact** is when 2 bodies touch (which may or may not result in a collision). If you are interested in being notified for contacts between bodyA and bodyB, you only need to set bodyA to contact bodyB i.e. set bodyA's `contactTestbitMask` to bodyB's categorybitMask - you don't need to set bodyb to contact bodyA. – Steve Ives Apr 19 '16 at 10:17

1 Answers1

1

Try this code for your didBeginContact:

- (void)didBeginContact:(SKPhysicsContact *)contact
{
    NSLog(@"bodyA:%@ bodyB:%@",contact.bodyA.node.name, contact.bodyB.node.name); // <- this gives you who touched who by object name

    uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);

    if (collision == (towerCategory | creepCategory))
    {
        NSLog(@"we got contact...");
    }
}

Remember that contact.bodyA.node.(property) can give you any property of SKNode. Things like name, position, xScale, yScale, alpha, children, parent and so on. You can see them all listed at the SKNode Class Reference.

sangony
  • 11,636
  • 4
  • 39
  • 55
  • Tested and worked awesome, but just to point out I solved my own problem above by figuring out the bitMasking was indeed higher for the tower node. I don't want post my response as an Answer just yet since I am in no position to give a lesson on bitMasking and the way they are arranged (higher values versus lower), but that the best answer I would like to accept for this. Essentially I just had the nodes mixed up when it was trying to removeAllActions from it. – Klutch Apr 17 '14 at 01:06
  • That's excellent. I'm happy I got you pointed in the right direction. Happy coding! – sangony Apr 17 '14 at 03:57
  • I much prefer this approach as it saves working out which body's category is higher/lower and also saves messing around with firstBody/secondbody and the complicated "if firstbody | secondBody) && ..." etc etc. – Steve Ives Apr 19 '16 at 10:18
  • @sangony - you might confuse people by calling the result of the AND between bodyA and bodyB 'collision', as a collision has a defined meaning within SpriteKit and here we are dealing with contacts, not collisions. (I use contactMask for this value, but that's just me). – Steve Ives Apr 19 '16 at 10:28