3
votes

I'm trying to create a shield power up for my iOS spritekit game and instead of refreshing the current shield, it stacks another shield on top. My code:

    -(void)didMoveToView:(SKView *)view {
               ...
    self.shieldIsActive = NO;
               ...
}
    -(void)didBeginContact:(SKPhysicsContact *)contact{
               ...
   else if (firstBody.categoryBitMask == CollisionCategoryLaser | firstBody.categoryBitMask == CollisionCategoryPlasmaShot |firstBody.categoryBitMask == CollisionCategoryProjectile && secondBody.categoryBitMask == CollisionCategoryShieldIcon){
        // Projectile OR Plasma Shot Or Laser hits Shield Icon
        ShieldIconNode *shieldIcon = (ShieldIconNode *)secondBody.node;
        ShieldNode *shield = [ShieldNode shieldAtPosition:CGPointMake(CGRectGetMidX(self.frame)+4, CGRectGetMidY(self.frame)-210)];
        [shieldIcon removeFromParent];
        if (firstBody.categoryBitMask == CollisionCategoryProjectile){
            ProjectileNode *projectile = (ProjectileNode *)firstBody.node;
            [projectile removeFromParent];
        }
        if (!self.shieldIsActive){
            [self addChild:shield];
            self.shieldIsActive = YES;
        }
        // Remove shield after a period of time. BUG: Adds multiple shields if one is already active
        if (self.shieldIsActive){
            SKAction *wait = [SKAction waitForDuration:ShieldTimer];
            [self runAction:wait completion:^{[shield removeFromParent];}];
            self.shieldIsActive = NO;

               ...
    }

2
What is your condition? (Projectile OR Plasma Shot OR Laser AND Shield Icon) or (Projectile OR Plasma Shot OR Laser) AND Shield IconDominique Vial
If the answer is the second proposition then you must encapsulate OR operations before applying ANDDominique Vial
@Exprosul One question (unrelated directly to stacking problem), why don't you use a property for a shield node and when shield is active, unhide it , and when is inactive just hide it (set hidden property to YES) ? Or this does not work for you (because you can have multiple shield instances at the same time on scene) ?Whirlwind
@Domsware, Thanks a lot, and ye it was the second proposition. I changed my code and now it makes a lot more sense.Exprosul
@Exprosuk I am not sure how stacking can happen if there is only one instance of a shield on the scene... But, if your current code works, then I guess there is no need for changes.Whirlwind

2 Answers

4
votes

Let's replace this condition:

else if (firstBody.categoryBitMask == CollisionCategoryLaser | firstBody.categoryBitMask == CollisionCategoryPlasmaShot |firstBody.categoryBitMask == CollisionCategoryProjectile && secondBody.categoryBitMask == CollisionCategoryShieldIcon){

by this:

else if ((firstBody.categoryBitMask == CollisionCategoryLaser || firstBody.categoryBitMask == CollisionCategoryPlasmaShot || firstBody.categoryBitMask == CollisionCategoryProjectile ) && secondBody.categoryBitMask == CollisionCategoryShieldIcon){

Notice the use of || instead of | for OR operator.

4
votes

As Domsware pointed out, the bitwise | needs to be replaced with logical || (and the appropriate parentheses added) in your first if statement. Oddly enough, since the | operator has a lower precedence than the == operator, your if statement may have worked as you expected.

Also, you should consider hiding/showing a shield instead of adding/removing shields as Whirlwind suggested.

That said, some thoughts...

  1. The conditions for if (!self.shieldIsActive) and if (self.shieldIsActive) will always be true (unless shieldIsActive is set somewhere else in the code), which may result in more than one shield in the scene at a time
  2. Moving self.shieldIsAction = NO into the action's completion block will resolve this issue

and some code...

    if (!self.shieldIsActive){
        ShieldNode *shield = [ShieldNode shieldAtPosition:CGPointMake(CGRectGetMidX(self.frame)+4, CGRectGetMidY(self.frame)-210)];
        [self addChild:shield];
        self.shieldIsActive = YES;
        SKAction *wait = [SKAction waitForDuration:ShieldTimer];
        [self runAction:wait completion:^{
            [shield removeFromParent];
            self.shieldIsActive = NO;
        }];
    }