1
votes

I am creating a game using sprite kit but I seem to have trouble with the bodyWithTexture when using it with collisions. bodyWithRectangle and circleOfRadius work fine, but when i use bodyWithTexture it looks like the didBeginContact method is being called more than once.

here is an example of the code i'm using

-(SKNode *) createPlayer
{

    level3Player = [SKNode node];
    player3Sprite = [SKSpriteNode spriteNodeWithImageNamed:@"character.png"];
    level3Player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:player3Sprite.size.width/2];
    level3Player.physicsBody.categoryBitMask = playerCategory3;
    level3Player.physicsBody.contactTestBitMask = platformCategory3 | rockCategory;

    [level3Player setPosition:CGPointMake(self.size.height/2, screenHeightL3 *11)];
    level3Player.physicsBody.affectedByGravity = NO;
    player3Sprite.physicsBody.dynamic = YES;
    level3Player.zPosition = 2;
    [level3Player setScale:0.6];


    [level3Player addChild:player3Sprite];

    return level3Player;

}


-(void) addRocksL3
{

    int randomNumber = arc4random_uniform(300);

    rock1 = [SKSpriteNode spriteNodeWithImageNamed:@"AsteroidFire.png"];
    rock1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:rock1.size.width/2];
    rock1.position = CGPointMake(self.size.width * 3, randomNumber);
    rock1.physicsBody.categoryBitMask = rockCategory;
    rock1.physicsBody.contactTestBitMask = playerCategory3;
    rock1.physicsBody.dynamic = NO;
    rock1.physicsBody.affectedByGravity = NO;
    rock1.zPosition = 2;
    [rock1 setScale:0.3];

    [foregroundLayerL3 addChild:rock1];


    [self addChild:rock1];

}



-(void) didBeginContact:(SKPhysicsContact*) contact
{
    SKPhysicsBody *firstBody, *secondBody;

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

    if((secondBody.categoryBitMask ==  platformCategory3) | redPlatformCategory)
    {

        level3Player.physicsBody.velocity = CGVectorMake(0, 100);
        level3Player.physicsBody.affectedByGravity = YES;

        player3Sprite.texture = [SKTexture textureWithImageNamed:@"goo5.png"];

        SKAction *sound1 = [SKAction playSoundFileNamed:@"squish.wav" waitForCompletion:NO];
        [self runAction:sound1];

        gestureRec3.enabled = YES;
    }


    if(secondBody.categoryBitMask == rockCategory)
    {
        gestureRec3.enabled = YES;
        playerL3.physicsBody.velocity = CGVectorMake(0, 200);
        SKAction *playSound = [SKAction playSoundFileNamed:@"Hurt.wav" waitForCompletion:NO];
        [self runAction:playSound];
        hitCountL3++;
    }

    switch (hitCountL3)
    {

        case 1:
            [health1Level3 removeFromParent];
            [self healthNodelevel31];

            break;

        case 2:
            [hit1L3 removeFromParent];
            [self healthNodeLevel32];

            break;

        case 3:
            [hit2L3 removeFromParent];

            player3Sprite.texture = [SKTexture textureWithImageNamed:@"splat.png"];
            [self gameOverSplatLevel3];
            didDie3 = true;
            SKAction *playSplat = [SKAction playSoundFileNamed:@"splat.wav" waitForCompletion:NO];
            [self runAction:playSplat];

            break;
    }    

when i use this code my character will sometimes take 1 hit and sometimes take all 3 hits when i collide with the rock. I could use circleOfRadius which works fine, but it's not what I am really looking for. Is there anyway i could use bodyWithTexture so my character only takes 1 hit each time?

2
That doesn't look like valid bitmask testing code. - trojanfoe
Where does cirlceOfRadius or bodyWithRectangle come from? You are not even showing how you are creating your character node. - El Tomato
Just added my character and rock methods. I have put NSLogs in my all my collisions and the code seems to run. as i say, the collisions seem to work fine with bodyWithRect/circeOfRadius but not with bodyWithTexture? - Hunter23

2 Answers

0
votes

If you are experiencing multiple collisions eg. didBeginContact is called multiple times, you have few options...

Without looking at your code, lets say you have a player and a rock. Each time when player collides with rock you want to remove the rock. So, you remove the rock from its parent. But before that, you make this change in your code (pseudo code):

if([rockNode parent]){
    [rockNode removeFromParent];
 }

The other way would be to subclass SKSpriteNode and make a Rock class and to make a custom boolean property which will change its value once when first collision happens. But this is just unnecessary complication:

if(rockNode.canCollide){
    [rockNode removeFromParent];
    rockNode.canCollide = NO;
}
0
votes

I had many problems with correct amount of collisions. Sometimes it would get one, sometimes none. So I tried this and it works. The only thing to change is in didBeginContact method.

I will presume that you declared categories like this:

//define collision categories
static const uint32_t category1    = 0x1 << 0;
static const uint32_t category2    = 0x1 << 1;
static const uint32_t category3    = 0x1 << 2;

Try to replace your code in didBeginContact with this one. I remember that correct collisions finally got to work after I did this.

-(void)didBeginContact:(SKPhysicsContact *)contact
{

 SKNode *newFirstBody = contact.bodyA.node;
 SKNode *newSecondBody = contact.bodyB.node;

uint32_t collision = newFirstBody.physicsBody.categoryBitMask | newSecondBody.physicsBody.categoryBitMask;

if (collision == (category1 | category2))
{
    NSLog(@"hit");
}

}

Hope it helps