简体   繁体   中英

Box2D Null Fixture in contact handler after deletion

I am using Libgdx's box2d implementation.

I am encountering a null pointer error after I delete a body from the world. I've tracked the problem down to my contact listener, where I am testing the fixture's filter against the other fixture to see if I should process the collision. The problem is that one of the fixtures is null. Initially you would think, okay so only process the collision for the non-null fixture, but if I don't know what the fixture was that ended contact, it becomes impossible to know what is and is not contacting the non-null fixture.

Before I get into the code, I promise that making the actual world.destroyBody(body) call outside of the physics step by adding the body to a queue to be processed later. Additionally the body gets queued in the update loop, which happens before the world.step()

(Note: Entities are basically wrappers for bodies)

Here is the main update loop

@Override
public void render() {
    processDestroyQueue();

    sm.update();   //Calls entity update functions, where the entity is added to the q
    camera.update();


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

    //... removed rendering stuff
}

Here is the add to queue function and processDestroyQueue()

public void destroy(Entity ent){
    destroyQueue.add(ent);
}

private void processDestroyQueue(){
    Entity ent;
    while((ent = destroyQueue.poll()) != null){
        world.destroyBody(ent.body());
        ent.dispose();
        entities.remove(ent);
    }
}

Finally here is the contact handler

public class ContactHandler implements ContactListener {

@Override
public void beginContact(Contact contact) {
    Fixture fixA = contact.getFixtureA();
    Fixture fixB = contact.getFixtureB();

    parseContact(true, fixA, fixB, contact);
}

@Override
public void endContact(Contact contact) {
    Fixture fixA = contact.getFixtureA();
    Fixture fixB = contact.getFixtureB();

    parseContact(false, fixA, fixB, contact);
}

@Override
public void preSolve(Contact contact, Manifold oldManifold) {
    // TODO Auto-generated method stub

}

@Override
public void postSolve(Contact contact, ContactImpulse impulse) {
    // TODO Auto-generated method stub

}

public void parseContact(boolean begin, Fixture fixA, Fixture fixB, Contact contact){
    System.out.println("FixtureA: " + fixA);
    System.out.println("FixtureB: " + fixB);
    if( (fixA.getFilterData().categoryBits & fixB.getFilterData().maskBits) != 0 ){
        sendContact(begin, fixA, fixB, contact);
        sendContact(begin, fixB, fixA, contact);
    }else if( (fixB.getFilterData().categoryBits & fixA.getFilterData().maskBits) != 0 ){
        sendContact(begin, fixA, fixB, contact);
        sendContact(begin, fixB, fixA, contact);
    }
}

public void sendContact(boolean begin, Fixture fixA, Fixture fixB, Contact contact){
    Object dataA = fixA.getUserData();
    Object dataB = fixB.getUserData();

    if(dataA instanceof FixtureData && dataB instanceof FixtureData){
        FixtureData fixtureDataB = (FixtureData)dataB;
        FixtureData fixtureDataA = (FixtureData)dataA;
        fixtureDataA.contact(fixB, contact, begin);
        fixtureDataB.contact(fixA, contact, begin);
    }
}

}

Lastly the error:

Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.gearworks.game.ContactHandler.parseContact(ContactHandler.java:42)
at com.gearworks.game.ContactHandler.endContact(ContactHandler.java:24)
at com.badlogic.gdx.physics.box2d.World.endContact(World.java:903)
at com.badlogic.gdx.physics.box2d.World.jniDestroyBody(Native Method)
at com.badlogic.gdx.physics.box2d.World.destroyBody(World.java:322)
at com.gearworks.Client.processDestroyQueue(Client.java:189)
at com.gearworks.Client.render(Client.java:118)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:206)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)

From what it looks like to me Box2D simply erases the fixture and then dispatches its end contact event. I need That fixture to be in the end contact even so that I know which fixture has completed its contact. Is my implementation wrong? Or is this just how box2D works and I need to figure out some other method?

It was indeed a bug in libgdx:

https://github.com/libgdx/libgdx/issues/1381

https://github.com/libgdx/libgdx/pull/1837

I think you just need a newer version of the source.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM