Delete all box 2D bodies from world in libGDX

3.7k Views Asked by At

Recently I have been trying to delete all my bodies from Box 2D world and I ran into a little bit of a trouble.

Here is my code for deleting all the bodies:

@Override
public boolean keyDown(int keycode) {

  if(keycode==Keys.R){

      LevelHolder.clearLevel();

  }

}

...

public static void clearLevel(){

    System.out.println("deleting bodies");
    Array<Body> bodies = new Array<Body>();
    world.getBodies(bodies);
    for(Body bod: bodies){
        world.destroyBody(bod);
    }
    System.out.println("deleted bodies");

}  

And it seems like a reasonable peace of code to me however this crashes sometimes (the message "deleted bodies" never gets printed) with error message from native code:

Assertion failed: (m_bodyCount > 0), function DestroyBody, file /Users/badlogic/jenkins/workspace/libgdx-mac/extensions/gdx-box2d/gdx-box2d/jni/Box2D/Dynamics/b2World.cpp, line 133. 

When deleting all bodies that I just got from the world Im somehow deleting more bodies than there are in the world. Also it doesn't happen all the time.

The piece of code is completely isolated and my game has no multi threading. So nothing could be deleting bodies while I'm stepping through all the bodies in the world.

What could be happening here? Maybe this is not the right way of deleting all the bodies and someone could give me an insight of how to do it better? Thank you.

Using libGDX 1.5.2 here.

3

There are 3 best solutions below

1
On

Try to get the count of bodies first, and then write a for loop that goes through the array using that count, rather than an enumerator. Editing a collection while it's being enumerated can cause issues.

3
On

The problem is that you are trying to delete the bodies in the keydown event so the world may be locked. What you should do is delete them in the render method.

    boolean clearLevel=false;
        @Override
        public boolean keyDown(int keycode) {
            if(keycode==Keys.R){
                clearLevel=true;
            }
        }

        ...

        public boolean render(float delta){
            world.step(delta, 8, 4);

            if(clearLevel){
                System.out.println("deleting bodies");
                Array<Body> bodies = new Array<Body>();
                world.getBodies(bodies);
                for(Body bod: bodies){
                    world.destroyBody(bod);
                }
                System.out.println("deleted bodies");
                clearLevel=false;
            }
        }
0
On

I was using the same code as you to destroy all bodies and ran into a similar problem. I believe the problem is that you are trying to destroy bodies while your box2d.World is locked(you can check with world.isLocked()). During your world.step() your box2d.World is locked. This was my problem, as I was trying to destroy bodies as a result of a beginContact() event and couldn't because this was being handled within world.step().

My solution was to assign a boolean flag to true and just before doing world.step() i would check to see if it was true and if it was, then I destroy all bodies using same code as you did above.

Here is an example i tested:

private boolean destroyAllBodies;

public void clearBodies()
{
    destroyAllBodies = true;
}

public void update()
{
    if(destroyAllBodies)
    {
        Array<Body> bodies = new Array<Body>();
        world.getBodies(bodies);
        for(int i = 0; i < bodies.size; i++)
        {
            if(!world.isLocked())
                    world.destroyBody(bodies.get(i));
        }
        destroyAllBodies = false;
    }

    ...

    world.step(1/60f, 6, 2);

    ...
}

I hope that helped. Cheers!