Question

I'm using Box2D and Cocos2D for iOS.

From time to time, the game freezes and it is caused by an infinite loop on b2World::SolveTOI.

for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next)
    {
        // Invalidate TOI
        c->m_flags &= ~(b2Contact::e_toiFlag | b2Contact::e_islandFlag);
        c->m_toiCount = 0;
        c->m_toi = 1.0f;
    }

I have a game where a "hero" has to collect coins. Coins are bodies, and through the contact listener I'm adding bodies in an array in order to destroy them later when the array is full (count = 2).

Here is how I add bodies to the array:

+ (void) addBodyToDestroy:(b2Body *)body {
[toDestroyArray addObject:[NSValue valueWithPointer:body]];
}

And here is how I destroy them:

+ (void) destroyAllBodies {

b2World *world = [InGame getWorld];

for (NSValue *bodyValue in toDestroyArray)
{
    b2Body *body;
    body = (b2Body*)[bodyValue pointerValue];
    world->DestroyBody(body);
    body = NULL;
}

[toDestroyArray removeAllObjects];
}

What I find very weird is that it doesn't freeze every time, just sometimes, and I can't get to know what seemes to block...

Solution : The body was added more than once into the array to be destroyed, so when the array was full, it was destroyed more than once, which caused the infinite loop.

Was it helpful?

Solution

  1. b2Contact of the contact manager is linked list data. So, for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) means start from m_contactList and loop until c->next meets NULL

  2. I don't know how your contact listener works, but you have to pay attention to the timing of the world step, collision check and destroy. Because, the contact listener is called each time when there's a contact while world is stepping. So if your contact listener is designed to store contact data, then you have to process all the contact data between world step and destroy. (If you don't, there might be dangling pointers of the destroyed body in contact data)

  3. If you want to destroy coins when they fill the array, you'd better check the array if there is same object.

OTHER TIPS

for (b2Contact* c = m_contactManager.m_contactList; c; c = c->m_next) is quite an unusual for loop, at least from my experience. I can clearly explain what it does, and maybe this will help you.

This is how a for loop works:

for( /*code called at beginning, usually to create a counting variable*/;/*code that is checked for a boolean value every loop.  True means loop again, false means stop.  Usually this is checking the value of the counting variable.*/;/*code that is called at the end of each loop.  Usually this is advancing the counting variable*/) {
//Code inside the loop that does stuff many times
}

So for this loop:

  1. The loop starts. a new b2Contact pointer named c is created and is assigned to the value of the m_contactList property of m_contactManager.

  2. The loop checks the value of c and determines a value of either true or false. In this case, since c appears to be an instance of an object, it probably is checking whether c is nil or not.

  3. At the end of each loop, c is set to point to the m_next member of c. This is quite strange as c is no longer m_contactList but something contained by m_contactList. Then again this could be perfectly normal. You should know what your code does.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top