Collision detection with NSMutablearray of objects

advertisements

I'm attempting to make a game with objects that bounce between the left and right walls of the screen until they reach the bottom.

I create my objects inside these methods which stores the objects in an NSMutable array and is called when the game begins.

-(void)createContent
{
    self.backgroundColor = [SKColor colorWithRed:0.54 green:0.7853 blue:1.0 alpha:1.0];

    world = [SKNode node];
    [self addChild:world];

    crabs = [NSMutableArray new];
    [self performSelector:@selector(createCrabs) withObject:nil afterDelay:1];
 }

-(void)createCrabs {
     crab = [HHCrab crab];
    [crabs addObject:crab];
    [world addChild:crab];
    crab.position = CGPointMake(world.scene.frame.size.width/12 , world.scene.frame.size.height/3.2);
    [crab moveLeft];

    //Next Spawn
    [self runAction:[SKAction sequence:@[
            [SKAction waitForDuration:10],
            [SKAction performSelector:@selector(createCrabs) onTarget:self],
    ]]];
}

This will endlessly create new objects, however a problem begins with collision detection. Originally I had my collision detection set up like this:

-(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 (firstBody.categoryBitMask==crabCategory && secondBody.categoryBitMask == leftWallCategory) {
        NSLog(@"Crab Hit Left Wall");
        [crab stop];
        [crab moveRight];
    } else if (firstBody.categoryBitMask == crabCategory && secondBody.categoryBitMask == rightWallCategory) {
        NSLog(@"Crab Hit Right Wall");
        [crab stop];
        [crab moveLeft];
    }
}

But, after more than one object is on the map, when the original object collides with a wall it begins to glitch and stop moving. This results in a pile up which bugs the new objects being spawned. I've also tried to use the update CurrentTime method to see if the collision detection would improve, however as predicted, only one object will move at a time while the rest will stay still.

-(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 (firstBody.categoryBitMask==crabCategory && secondBody.categoryBitMask == leftWallCategory) {
        NSLog(@"Crab Hit Left Wall");
        self.crabTouchLeftWall = YES;
    } else if (firstBody.categoryBitMask == crabCategory && secondBody.categoryBitMask == rightWallCategory) {
        NSLog(@"Crab Hit Right Wall");
        self.crabTouchRightWall = YES;
    }
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
    for (HHCrab *crabNode in crabs){
        if (self.crabTouchLeftWall){
           [crabNode stop];
            [crabNode moveRight];
            self.crabTouchLeftWall = NO;
        }
        if (self.crabTouchRightWall){
            [crabNode stop];
            [crabNode moveLeft];
            self.crabTouchRightWall = NO;
        }
    }
}

How can I fix this so when one object collides with the wall, it does not effect the movement of the other objects, only itself?


There are a couple of suggestions I have.

As you are creating multiple instances of crab and adding them into the crabs array, I suggest you give each crab a unique name property. Your can do this by having a running int counter. For example:

@implementation GameScene {
    int nextObjectID;
}

Then when you create a crab instance:

nextObjectID++;
[crab setName:[NSString stringWithFormat:@"crab-%i",nextObjectID]];

I personally prefer coding my didBeginContact like this:

- (void)didBeginContact:(SKPhysicsContact *)contact {
    uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);

    if (collision == (crabCategory | leftWallCategory)) {
        // figure out which crab in the array made the contact...
        for(HHCrab *object in crabs) {
            if(([object.name isEqualToString:contact.bodyB.node.name]) || ([object.name isEqualToString:contact.bodyA.node.name])) {
                NSLog("I am %@",object.name);
            }
        }
    }
}

Your code self.crabTouchLeftWall = YES; is not the right way to go. You should create a crab class property and set that to YES. Reason being that each crab instance needs to have these properties. The way you currently have it, all crab instances all share the same BOOLs self.crabTouchLeftWall and self.crabTouchRightWall.

You are checking for the BOOL values contacts in the update method and then running [crabNode stop];. You could do this directly when the contact is registered in the didBeginContact method.

The reason I earlier suggested you use unique names for each crab instance is because when it comes time to remove them from your array, you need to be able to point to which specific crab instance needs removing. Having a unique name just makes things easier.