2
votes

I am developing a game using LibGDX and Box2D, today I implemented coins as dynamic bodies and wanted to make player gain gold by reading contact through my ContactListener.

So far everything was working, here is my example of player colliding with ladder object in the ContactListener class:

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

    int cDef = fixA.getFilterData().categoryBits | fixB.getFilterData().categoryBits;


    switch (cDef) {
        case Constants.PLAYER_BIT | Constants.LADDER_BIT:
            if (fixA.getFilterData().categoryBits == Constants.PLAYER_BIT) {
                    ((HeroKnight) fixA.getUserData()).climbLadder();
            }
            else {
                    ((HeroKnight) fixB.getUserData()).climbLadder();
                }

However, strangely the coin collision only works one way.

case Constants.PLAYER_BIT | Constants.COIN_BIT:
            if (fixA.getFilterData().categoryBits == Constants.PLAYER_BIT) {
                ((CoinTest) fixB.getUserData()).use();
            }

When I add the else statement, as seen below, the game keeps crashing with java.lang.NullPointerException.

case Constants.PLAYER_BIT | Constants.COIN_BIT:
            if (fixA.getFilterData().categoryBits == Constants.PLAYER_BIT) {
                ((CoinTest) fixB.getUserData()).use();
            }
            else  {
                ((CoinTest) fixA.getUserData()).use();
            }

The player class's fixturedef maskbits include coin, and coin class's maskbits include the player (everything is done the same way as with ground, platforms, ladders etc., and the problem only exists here).

I hope I explained this well enough, this is my first question here.

1
The question is well explained, although the last missing bit is the line the exception occurs at from stacktrace. The only thing I can see you added, which may produce an NPE is that fixA.getUserData() may return null and throw when you try to invoke use() at it. - pafau k.
The NPE occurs at this line in the else statement : ((CoinTest) fixA.getUserData()).use(); . I can't understand why this line would produce a null value. My case checks if there's a contact between 2 objects and if they're a pair of player and coin. So if one is a player, the other one has to be a coin? Also it doesn't make sense for me why only this player/coin contact has this issue. I have no idea how to fix this. Any ideas? - Deso2121
I agree with @pafauk. , it looks like the A fixture does not have user data set. Can you share the code where you set it? Are you only setting it on the Body and not the Fixture? - bornander
@bornander are you referring to A fixture as in Fixture fixA = contact.getFixtureA(); created in beginContact method or the Coin class fixture? - Deso2121
How many fixtures are your bodies made up of, and where are you storing your player user data? The fixture might be part of your player for example, but it might not be the place you put your user data. - Laurence

1 Answers

1
votes

I finally fixed this. There was such a rookie mistake, I totally overlooked it and didn't think that this would create such problems. The solution was just to add "break" statements in the ContactListener. This made it work without any problems:

       switch (cDef) {
        case Constants.PLAYER_BIT | Constants.LADDER_BIT:
            if (fixA.getFilterData().categoryBits == Constants.PLAYER_BIT) {
                    ((HeroKnight) fixA.getUserData()).climbLadder();
            }
            else {
                ((HeroKnight) fixB.getUserData()).climbLadder();
                }
            break;
        case Constants.PLAYER_BIT | Constants.GROUND_BIT:
        case Constants.PLAYER_BIT | Constants.PLATFORM_BIT:
            if (fixA.getFilterData().categoryBits == Constants.PLAYER_BIT) {
                ((HeroKnight) fixA.getUserData()).ground();
            }
            else {
                ((HeroKnight) fixB.getUserData()).ground();
            }
            break;
        case Constants.PLAYER_BIT | Constants.COIN_BIT:
            if (fixA.getFilterData().categoryBits == Constants.PLAYER_BIT) {
                ((CoinTest) fixB.getUserData()).use();
                ((HeroKnight) fixA.getUserData()).collectGold(10);
            }
            else  {
                ((CoinTest) fixA.getUserData()).use();
                ((HeroKnight) fixB.getUserData()).collectGold(10);
            }
            break;
    }
}