Search code examples
javanullcollision-detectionjbox2d

JBox2D Body Was There, Now it's null?


A strange bug I can't track down. I've implemented a ContactListener class for handling collisions in my Android game. In the beginContact(Contact arg0) method I can see that the two bodies in arg0 is there, and pushed onto a stack. Right after calling world.step() I run my own handleCollisions() method, where I pop off the Contact objects and do some game logic. However, occasionally when I pop off a Contact, one or both of its bodies are null.

The Contact goes in the stack with its bodies there, but it comes out with null bodies. I don't know why this is occurring, and more importantly, I can't find when this is occurring. To my knowledge, none of my code elsewhere is removing the bodies, but then again there could be side effects I'm unaware of. It doesn't help that this doesn't always happen. Typically it occurs when there's several collisions occurring.

Anyone have any ideas on what might remove the bodies? Or, does anyone know of a way to track the bodies to determine when they become null?

Below is some code which may or may not be helpful:

public class ConcreteContactListener implements ContactListener
{
    private Stack<Contact> contacts;

    public ConcreteContactListener()
    {
        contacts = new Stack<Contact>();
    }

    @Override
    public void beginContact(Contact arg0)
    {
        contacts.push(arg0);
        System.out.println("push: " + arg0.m_fixtureA.m_body);
    }


public int handleCollisions(ArrayList<Ball> balls, World world, ArrayList<Ball> smears, SoundEffects sfx, Combos combos)
{
    int score = 0;

    while (!contacts.isEmpty())
    {
        Contact contact = contacts.pop();
        System.out.println("Contact: " + contact.m_fixtureA.m_body);
        int a = -1;
        int b = -1;

        for (int i = 0; i < balls.size(); i++)
        {
            System.out.println("Ball: " + balls.get(i).getBody());
            if (contact.m_fixtureA.m_body.equals(balls.get(i).getBody()))
                 a = i;
            else if (contact.m_fixtureB.m_body.equals(balls.get(i).getBody()))
                b = i;
        }

        ...

    }

}

Solution

  • Contacts are pooled and re-used, so I would not recommend using this approach. Instead I would buffer only the information you need (which is probably the two bodies). The jbox2d testbed handles it this way:

    First we have a contact point:

    public class ContactPoint {
        public Fixture fixtureA;
        public Fixture fixtureB;
        public final Vec2 normal = new Vec2();
        public final Vec2 position = new Vec2();
        public PointState state;
    }
    

    And then we listen like so:

    public void beginContact(Contact contact) {
    }
    
    public void endContact(Contact contact) {
    }
    
    public void postSolve(Contact contact, ContactImpulse impulse) {
    }
    
    private final PointState[] state1 = new PointState[Settings.maxManifoldPoints];
    private final PointState[] state2 = new PointState[Settings.maxManifoldPoints];
    private final WorldManifold worldManifold = new WorldManifold();
    
    public void preSolve(Contact contact, Manifold oldManifold) {
        Manifold manifold = contact.getManifold();
    
        if (manifold.pointCount == 0) {
            return;
        }
    
        Fixture fixtureA = contact.getFixtureA();
        Fixture fixtureB = contact.getFixtureB();
    
        Collision.getPointStates(state1, state2, oldManifold, manifold);
    
        contact.getWorldManifold(worldManifold);
    
        for (int i = 0; i < manifold.pointCount
                && pointCount < MAX_CONTACT_POINTS; i++) {
            ContactPoint cp = points[pointCount];
            cp.fixtureA = fixtureA;
            cp.fixtureB = fixtureB;
            cp.position.set(worldManifold.points[i]);
            cp.normal.set(worldManifold.normal);
            cp.state = state2[i];
            ++pointCount;
        }
    }
    

    This would probably be a bit overkill for your purposes, as it executes this logic for every single contact. Instead you can use the beginContact() and endContact() methods and buffer something a little more optimized with your game, like just storing the colliding bodies or something.