Oscar Apeland
Oscar Apeland

Reputation: 6662

NSArray removeObject removes all objects in array

My project creates a bomb, an explosion, then checks for collisions in the explosions and finally delete the bombs that didn't get hit in a collision. This is explained in more detail here. The following code does this.

-(void)placeBomb
{
    NSLog(@"Bomb placed");
    _circle = [[CCSprite alloc]initWithFile:@"Circle.png"];
    CGPoint circle0position = ccp(_cat.position.x , _cat.position.y);
    CGPoint c0TileCoordt = [self tileCoordForPosition:circle0position];
    CGPoint c0TileCoord = [self positionForTileCoord:c0TileCoordt];
    _circle.position = c0TileCoord;
    [self addChild:_circle];
    id fade = [CCScaleTo actionWithDuration:3.5 scale:0];
    [_circle runAction:fade];
    
    double delayInSeconds = 3.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self explosionFromPoint:c0TileCoordt withSprite:_circle];
    });
    
    
}

- (BOOL)isLocationBombable:(CGPoint)tileCoord;
{
    if ([self isValidTileCoord:tileCoord] && ![self isWallAtTileCoord:tileCoord])
    {
        return YES;
        
    }
    else
    {
        return NO;
    }
}

-(void)explosionFromPoint:(CGPoint)explosionPoint withSprite:(CCSprite*)sprite;
{
    //int 
    explosionLenght += 1;
    if (explosionLenght >= 7) //Just for testing purposes, don't have a way to increase it naturally. 
    {
        explosionLenght = 1;
    }
        
    BOOL topB = YES;
    BOOL leftB = YES;
    BOOL bottomB = YES;
    BOOL rightB = YES;
    
    int bombX =    (explosionPoint.x + 1);
    int bombY =    (explosionPoint.y + 1);
    int bombNegX = (explosionPoint.x - 1);
    int bombNegY = (explosionPoint.y - 1);
    
    CGPoint top = ccp(explosionPoint.x, bombY);
    CGPoint left = ccp(bombNegX, explosionPoint.y);
    CGPoint bottom = ccp(explosionPoint.x, bombNegY);
    CGPoint right = ccp(bombX, explosionPoint.y);
    
    if (![self isLocationBombable:top])
    {topB = NO;}
    if (![self isLocationBombable:left])
    {leftB = NO;}
    if (![self isLocationBombable:bottom])
    {bottomB = NO;}
    if (![self isLocationBombable:right])
    {rightB = NO;}

    for (int i = 0; i <= explosionLenght; i++) {
                
        int bombX =    (explosionPoint.x + i);
        int bombY =    (explosionPoint.y + i);
        int bombNegX = (explosionPoint.x - i);
        int bombNegY = (explosionPoint.y - i);
        
        CGPoint top = ccp(explosionPoint.x, bombY);
        CGPoint left = ccp(bombNegX, explosionPoint.y);
        CGPoint bottom = ccp(explosionPoint.x, bombNegY);
        CGPoint right = ccp(bombX, explosionPoint.y);
        
        CCSprite *circleTop    = [[CCSprite alloc]initWithFile:@"Circle.png"];
        CCSprite *circleLeft   = [[CCSprite alloc]initWithFile:@"Circle.png"];
        CCSprite *circleBottom = [[CCSprite alloc]initWithFile:@"Circle.png"];
        CCSprite *circleRight  = [[CCSprite alloc]initWithFile:@"Circle.png"];
        int scaleTime = 5;
        if ([self isLocationBombable:top] && topB == YES)
        {
            circleTop.position = [self positionForTileCoord:top];
            [self addChild:circleTop];
            id fadeTop = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleTop.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
            [circleTop runAction:fadeTop];
        }
        if ([self isLocationBombable:left] && leftB == YES)
        {
            circleLeft.position = [self positionForTileCoord:left];
            [self addChild:circleLeft];
            id fadeLeft = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleLeft.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
            [circleLeft runAction:fadeLeft];
        }
        if ([self isLocationBombable:bottom] && bottomB == YES)
        {
            circleBottom.position = [self positionForTileCoord:bottom];
            [self addChild:circleBottom];
            id fadeBottom = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleBottom.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
            [circleBottom runAction:fadeBottom];
        }
        if ([self isLocationBombable:right] && rightB == YES)
        {
            circleRight.position = [self positionForTileCoord:right];
            [self addChild:circleRight];
            id fadeRight = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleRight.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
            [circleRight runAction:fadeRight];
        }
    }
    
    [currentBombs addObject:sprite];
    int a = [currentBombs count];
    NSLog(@"cBCount: %i",a);
    NSLog(@"Explosion done, call checkdamage");
    
    [self schedule:@selector(checkDamageForBomb)];
    [self performSelector:@selector(removeSprite:) withObject:sprite afterDelay:5];
}

-(void)removeSprite:(CCSprite *)sprite{
    int aa = [currentBombs count];
    NSLog(@"removeSprite startcall cbc: %i",aa);

    
    if([currentBombs containsObject:sprite])
    {
        NSLog(@"Found sprite in array, deleted!");
        [currentBombs removeObject:sprite];
        int a = [currentBombs count];
        NSLog(@"containObject cbc: %i",a);
    }
    else {
        NSLog(@"Didn't find the object in array, didn't delete!");
        int a = [currentBombs count];
        NSLog(@"elseCO cbc: %i",a);
    }
    if (currentBombs.count == 0)
    {
        [self stopCheckDamage];

    }
    
}

-(void)stopCheckDamage{

    NSLog(@"StopCheckDamage");
    [self unschedule:@selector(checkDamageForBomb)];
    
}

-(void)checkDamageForBomb{
    for (CCSprite* bomb in currentBombs) 
    {
        CGPoint bombPos = [self tileCoordForPosition:bomb.position];
        
        for (int i = 0; i <= explosionLenght; i++) {
            
            CGPoint playerPos = [self tileCoordForPosition:_cat.position];
            
            int bombX =    (bombPos.x + i);
            int bombY =    (bombPos.y + i);
            int bombNegX = (bombPos.x - i);
            int bombNegY = (bombPos.y - i);
            
            CGPoint centre = bombPos;
            CGPoint top = ccp(centre.x, bombY);
            CGPoint left = ccp(bombNegX, centre.y);
            CGPoint bottom = ccp(centre.x, bombNegY);
            CGPoint right = ccp(bombX, centre.y);
        
            //pastebin.com/biuQBfnv
            
            if (CGPointEqualToPoint(top, playerPos) || CGPointEqualToPoint(left, playerPos) || CGPointEqualToPoint(bottom, playerPos) || CGPointEqualToPoint(right, playerPos))
            {
                playerHits += 1;
                NSLog(@"Player hit %i",playerHits);
                [currentBombs removeObject:bomb];
                break;
            }
        }
    }
}

My problem is with the -(void)removeSprite:(CCSprite *)sprite{method. This is supposed to delete only the one it got called with, but instead it kills them all, as you can see in this log.

15:14:02.499 Tile[1549:c07] Bomb placed
15:14:03.816 Tile[1549:c07] Bomb placed
15:14:05.501 Tile[1549:c07] cBCount: 1
15:14:05.501 Tile[1549:c07] Explosion done, call checkdamage
15:14:06.818 Tile[1549:c07] cBCount: 2
15:14:06.819 Tile[1549:c07] Explosion done, call checkdamage
15:14:06.819 Tile[1549:c07] CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: 0.00 to 0.00
15:14:10.503 Tile[1549:c07] removeSprite startcall cbc: 2 // has 2
15:14:10.503 Tile[1549:c07] Found sprite in array, deleted! //Line above and under
15:14:10.504 Tile[1549:c07] containObject cbc: 0 //Deleted 2, supposed to kill 1 
15:14:10.505 Tile[1549:c07] StopCheckDamage
15:14:11.820 Tile[1549:c07] removeSprite startcall cbc: 0
15:14:11.820 Tile[1549:c07] Didn't find the object in array, didn't delete!
15:14:11.821 Tile[1549:c07] elseCO cbc: 0
15:14:11.821 Tile[1549:c07] StopCheckDamage

If you look at the log location in the code above, you will see that it deletes two instead of the one I wanted to. How can I prevent this behaviour or customise it to only kill the proper sprite?

Edit: To clarify I thought that as I use the spritein the -(void)explosionFromPoint:(CGPoint)explosionPoint withSprite:(CCSprite*)sprite; this would somehow just give an unique ID to the object and know which one I was talking about. I'm new to coding.

Upvotes: 1

Views: 699

Answers (5)

Alok Singh
Alok Singh

Reputation: 896

Don't use class variable while creating bombs and try....

CCSprite * _circle = [[CCSprite alloc]initWithFile:@"Circle.png"];

Upvotes: 1

Hermann Klecker
Hermann Klecker

Reputation: 14068

You probably added the same (indiviudal) sprite twice?

Instead of logging a variable that has the count, you can log the object itself. It will print the value of its description. That will print out the class and address for all NSObject subclasses per default. Actually NSMutableArray (and similar NS... classes) print quite well.

NSLog ("%@",myObj); 

Doing so you probably see more clearly what really happens.

Upvotes: 1

Hot Licks
Hot Licks

Reputation: 47699

You have the same sprite in the array twice. Both entries are removed. If you're going to use removeObject you need to create multiple sprite objects. Otherwise you need to use removeObjectAtIndex.

Upvotes: 4

Divyu
Divyu

Reputation: 1313

There is something that is going wrong in your code. Try checking something like that when you remove object from array do this:

 if([currentBombs count]>1 )
    [currentBombs removeObjectAtIndex:1]; 

If your code works fine. that is only one object removed from your array. Then I suggest you to check your removeSprite method print sprite object to check what's going wrong.

I think you can use tag values if you using same sprite objects.

Upvotes: 2

giorashc
giorashc

Reputation: 13713

The problem as @HotLicks mentioned is you push the same instance in the array. You should use a local CCSprite instance so your scheduled calls will use different instances. The reason for adding the same instance twice is because placeBomb is called twice before anything happens and in the second call you override the first instance you created. Since _circle is a pointer by the time both scheduled tasks will be called _circle will point to the same instance in both.

So change :

_circle = [[CCSprite alloc]initWithFile:@"Circle.png"];

To :

CCSprite *circle = [[CCSprite alloc]initWithFile:@"Circle.png"];

and update the rest of the method with circle and not _circle.

Upvotes: 3

Related Questions