Предотвратить несколько обратных вызовов для одного столкновения с b2ContactListener? - PullRequest
0 голосов
/ 26 ноября 2011

Я скачал простой слушатель контактов Рэя Вендерлиха из этого учебного пособия: http://www.raywenderlich.com/606/how-to-use-box2d-for-just-collision-detection-with-cocos2d-iphone

В настоящее время я использую приведенный ниже код, который в значительной степени является реализацией пользовательского b2ContactListener Рэя в моем CCLayer.Но проблема в том, что существует несколько обратных вызовов для одного столкновения.

Может кто-нибудь показать мне, как сделать так, чтобы коллизия сработала только один раз, пока 2 объекта не отсоединились, а затем снова обработали?

Это код, которым я являюсьв настоящее время используется:

std::vector<b2Body *>toDestroy; 
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin(); pos != _contactListener->_contacts.end(); ++pos) {
    MyContact contact = *pos;

    // Get the box2d bodies for each object
    b2Body *bodyA = contact.fixtureA->GetBody();
    b2Body *bodyB = contact.fixtureB->GetBody();
    if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
        CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
        CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();

        if ((spriteA.tag == 1 && spriteB.tag == 2) || (spriteA.tag == 2 && spriteB.tag == 1)) {
             NSLog(@"tag 1 hit tag 2");
        } 

Спасибо!

Edit1:

if ((hasDetectedCollision == NO) && ( (spriteA.tag == 1 && spriteB.tag == 2) || (spriteA.tag == 2 && spriteB.tag == 1) ) ) {
                hasDetectedCollision = YES;
                NSLog(@"tag 1 hit tag 2");
                [self doSomethingWhenCollisionHappens];
            }

- (void)doSomethingWhenCollisionHappens {
    //here you might want to remove an object because of a collision
    //or maybe change the color of the objects that collided
    //I can't provide more details of what you might want
    //to do without seeing more of your code
    //but when you are ready to start receiving collision updates again
    //set hasDetectedCollision to NO again
    [yesLabel setString:[NSString stringWithFormat:@"Yes: %d", yesNumber++]];
    hasDetectedCollision = NO;
}

Тогда в моем методе инициализации:

hasDetectedCollision = NO;

НовыйИзменить:

if (!hasDetectedCollision && ((spriteA.tag == 1 && spriteB.tag == 4) || (spriteA.tag == 4 && spriteB.tag == 1))) {
            hasDetectedCollision = YES;
            [yesLabel setString:[NSString stringWithFormat:@"Yes:%d", yesInt++]];
            NSLog(@"tag 1 hit tag 4");
        } 

Ответы [ 2 ]

1 голос
/ 01 декабря 2011

Я не знаком с Box2d, но из того, что вы описали в своей проблеме, это должно вам помочь:

В вашем .h:

@interface foo : FooBar {
    BOOL hasDetectedCollision;
}

и в вашем.m:

- (void)viewDidLoad {
    hasDetectedCollision = NO;
}

, затем просто замените

if ((spriteA.tag == 1 && spriteB.tag == 2) || (spriteA.tag == 2 && spriteB.tag == 1)) {
    NSLog(@"tag 1 hit tag 2");
} 

на

if ((hasDetectedCollision == NO) && ( (spriteA.tag == 1 && spriteB.tag == 2) || (spriteA.tag == 2 && spriteB.tag == 1) ) ) {
    hasDetectedCollision = YES;
    NSLog(@"tag 1 hit tag 2");
    [self doSomethingWhenCollisionHappens];
} 

, а затем:

- (void)doSomethingWhenCollisionHappens {
    //here you might want to remove an object because of a collision
    //or maybe change the color of the objects that collided
    //I can't provide more details of what you might want
    //to do without seeing more of your code
    //but when you are ready to start receiving collision updates again
    //set hasDetectedCollision to NO again
    hasDetectedCollision = NO;
}

Затем, когда вывыполнив все, что вы хотите сделать, когда обнаружите столкновение, установите для hasDetectedCollision значение no, и вы можете начать заново.

Edit:

std::vector<b2Body *>toDestroy; 
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin(); pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;

// Get the box2d bodies for each object
b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
    CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
    CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();

    if(hasDetectedCollision == NO)
        NSLog(@"hasDetectedCollision == NO");

    if ((spriteA.tag == 1 && spriteB.tag == 2) || (spriteA.tag == 2 && spriteB.tag == 1)) {
         NSLog(@"tag 1 hit tag 2");
    } 

Если оператор if проверяет состояниеhasDetectedCollision выполняется, тогда проблема не в булевом, а в вашем обнаружении столкновений.

0 голосов
/ 01 декабря 2011

Я не совсем уверен, каковы правила SO для множественных ответов, но это альтернатива для проверки на наличие коллизий, которую я использую все время:

В вашем методе viewDidLoad:

NSTimer *g = [[NSTimer alloc] initWithFireDate:[NSDate date]
                                      interval:.1
                                        target:self
                                      selector:@selector(checkForCollisions)
                                      userInfo:nil repeats:YES];

NSRunLoop *r = [NSRunLoop currentRunLoop];
[r addTimer:g forMode: NSDefaultRunLoopMode];
[g release];

В вашем методе checkForCollisions:

//without knowing what you are checking collisions for and
//why you are checking for collisions, I can't be too specific here
//but this code will do what you want

for(someObject* objectOfInterest in yourArrayOfObjectsToBeCompared)) {
    CALayer* layerOne = objectOfInterest.layer.presentationLayer;
    CGRect rectOne = layerOne.frame;
    for(someObject* compareObject in yourArrayOfObjectsToBeCompared) {
        CALayer* layerTwo = compareObject.layer.presentationLayer;
        CGRect rectTwo = layerTwo.frame;
        if(CGRectIntersectsRect(rectOne, rectTwo) && ![compareObject isEqual:objectOfInterest]) {
            //collision detected
        }
    }

Этот код просто перебирает ваш массив объектов для сравнения, а затем создает копию CALayer для уровня представления каждого объекта. Этот шаг очень важен, потому что выбор обычного кадра объекта не даст его текущей позиции; это даст его место назначения. Затем код снова делает то же самое, сравнивая его с каждым другим объектом в массиве.

...