iosobjective-ccocos2d-iphonebox2dkobold2d

Box2d collision between dynamic objects


I'm trying to make a simple shooting game with Box2d.

Enemies and bullets are both dynamic bodies. I create enemies on the left side of screen, and then apply a linear force to them to make them move towards right. The player needs to hit an enemy by three times to kill it.

My problem is, how can I make the enemy keep moving when it is hit by bullets for the first two times? Right now, when the enemy is hit for the first time, it will be knocked down or even knocked out of the screen. I hope the enemy behaves like this only when it gets killed (be hit for the third time). How should I do that?

Thanks for any suggestion!

Some Code Here:

    // Apply force
    rock.isFlying = 1;
    b2Vec2 force = b2Vec2(-2.0f*distance*cos,-2.0f*distance*sin);
    rockBody->ApplyForce(force,rockBody->GetWorldCenter());
    rockBody->SetLinearDamping(0.2f);

- (void)birdFly:(ccTime)dt{
    for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
    {
        ZLBird *myBird = (__bridge ZLBird*)b->GetUserData();
        if (b->GetUserData() != NULL && myBird.tag == TAG_BIRD)
        {
            // Apply force
            b2Vec2 gravityOffset;
            gravityOffset.x = 0;
            gravityOffset.y = myBird.multiplier*(myBird.startY - myBird.position.y);
            if (!myBird.isDead)
                b->ApplyForce(gravityOffset , b->GetWorldCenter());
        }
    }
}

void ContactListener::BeginContact(b2Contact* contact)
{
    b2Body* bodyA = contact->GetFixtureA()->GetBody();
    b2Body* bodyB = contact->GetFixtureB()->GetBody();
    CCSprite* spriteA = (__bridge CCSprite*)bodyA->GetUserData();
    CCSprite* spriteB = (__bridge CCSprite*)bodyB->GetUserData();

    if (spriteA != NULL && spriteB != NULL)
    {
        // if hit by rock
        if (spriteA.tag == -1 && spriteB.tag == TAG_BIRD){
            ZLBird *myBird = (__bridge ZLBird*)bodyB->GetUserData();
            ZLRock *myRock = (__bridge ZLRock*)bodyA->GetUserData();
            [myBird getHurt];
        }
        else if(spriteB.tag == -1 && spriteA.tag == TAG_BIRD) {
            ZLBird *myBird = (__bridge ZLBird*)bodyA->GetUserData();
            ZLRock *myRock = (__bridge ZLRock*)bodyB->GetUserData();
            [myBird getHurt];
        }
}
}

Solution

  • preSolve and postSolve callback methods in contact listeners will be helpful here. You can set SetEnabled(false) in the preSolve() method so that box2d will not calculate required forces to be applied on the bodies. i.e. box2d will skip the collision. But note that this is applicable only for that particular time step duration only. In the next box2d time step the collision will be calculated. In order to skip the complete collision between rock and the bird for the first two times you need to call the SetEnabled(false) in every frame but with a check. So for e.g.

    Take an integer in the rock body's userdata. And in every time step you just call SetEnabled(false) and increment the integer until the integer becomes 2. Once this is 2 then next time onwards dont call SetEnabled(false). By default it will set to SetEnabled(true). And during the third hit the box2d will apply forces on the rock body.

    You must read http://www.iforce2d.net/b2dtut/collision-anatomy

    Specifically read the preSolve and postSolve callbacks.