Full LibGDX Game Tutorial – Box2D Contact Listener

box2d swimming

Sharing is caring!

Full LibGDX Game Tutorial – Box2D Contact Listener

Welcome to part 5 of our Full LibGDX Game Tutorial. This part will focus on the Box2D Contact Listener which will allow us to react to events such as objects hitting each other. If you haven’t seen the earlier parts of this tutorial I advise you to start at Full LibGDX Game Tutorial – Project setup as this tutorial continues from these earlier parts. For those of you who have come from part 4, you can continue on.

A contact listener allows you to react to collisions in your box2d world. Every time two bodies hit each other a method called beginContact is run and we can enter our own code inside this beginContact method to allows us to do things based on when a body hits another, all we have to do is check whether the body that is colliding is a body we want to change.

We should start by making our own class which will use the Box2D contact listener. We will call it B2dContactListener and it will take the B2dModel as an argument in the constructor. You should end up with the code below:

We should add some code to our beginContact method so we know when something starts colliding. For now, we will just add a message that gets printed out in our console. Update your beginContact method with the following code:

In this code, we have used “System.out.println(“text”);” to print a message out to the console. You can put whatever you want in the text part to output text to the console. This is very useful for debugging as it allows you to see what values are when your code is running. As well as console message code we have also used the contact objects getFixtureA and getFixtureB methods to get the two fixtures that have collided. We store them in fa and fb so we can print out the type, this should output DynamicBody or StaticBody depending on which objects are colliding.

If you run your application now you will see that nothing is being printed to the console. That is because the world doesn’t know to use the contact listener we have created. We have to tell the world to use our contact listener. To do that we will update our B2dModel. Update your B2dModel’s constructor so it adds the contact listener.

Now if you run your application you will see some output in the console:Contact Listener Eclipse Output

This is a good start, we can now add some code to cause our bodies to do things if they hit certain bodies. We currently don’t have a way to identify which bodies are hitting each other, that will come later when we start adding entities. for now let’s just get a feel for the basics of the contact listener.

We know that when the beginContact method is run that some bodies have collided. We also know that each of those bodies has a fixture which is the colliding part which we store in fa and fb. Remember a body is just the data part and the fixtures are the physical objects that collide. We are already getting they body type in the system out message,  we will be using this to make all bodies that hit the static floor shoot up in the air.

Update your beginContact method to the following:

Here, we are using an if statement to check if the body connected to fixture a is a StaticBody and if it is we get the body connected to fixture b and use the applayForcetoCentrer method to apply a huge force to make it go up and left. The first value makes it go left or right, negative values for left and positive values for right. The second value makes it go up or down, negative values for up and positive values for down. Thew final value is used to wake up the object. To increase performance Box2D will ignore bodies that haven’t moved for a long time, those are sleeping objects and need to be woken up to use.

Now if you run your application you will see that sometimes when the falling bodies hit the static floor they shoot up and sometimes they don’t. This is our fault. We only told it to check if fixture a was a static body. Sometimes fixture b will be the static body as the order of the bodies is not set and can come in any order. So how do we fix it so it will always shoot bodies up. We could add an extra if statement and repeat the code again, however, this mean repeating code and repeating code is not what we want to do. We should change it so we check if fixture a or fixture b is static then send them to a method to apply the forces. This means we don’t repeat code and any updates we do will be applied to both possibilities.

Let’s create the method first.

Here we create a private method(as no other classes will ever need to use it) that prints a message to the console and applies the forces to our body. We pass both the static fixture and the non-static fixture to this method then apply the force to the non-static one. If this was a game mechanic like a platform that disappears after one use we could make it shoot the player(non-static body) up in the air and remove the platform(static body). for now,  we will just shoot the body in the air.

Next, we will add the if statement to check if either of the fixtures are static.

We changed our beginContact method so now when a contact starts it checks if the body of Fixture a is static if it is then it uses our private method with fa as the first argument( the first argument is our static one). We then use “else if” to say if the first part is not true then check if fb is static and again we send it to our private method except this time fb is first. The final thing we add is the “else” part which says if none of the above statements are true then do this.

We have created a contact listener and got our world bodies to move when certain bodies collide  This is good as it allows us to be able to say that if our player character is on the ground we are allowed to jump but what if our player was swimming and we wanted to allow them to swim up when they’re in the water but not swim up when they are in the air. Well, we can use sensors. We already created the ability to make a cone sensor in our BodyFactory but we’re not going to use that as that was intended for use later to add a field of vision sensor. We’re going to add a new method to our BodyFactory that will turn fixture into sensors so we can add sensors wherever and whenever we like.

Open our BodyFactory class and add the following code:

The method code should be pretty standard by now but the code in the method is new. We have added a “for loop”. As the name implies, this will loop for a certain amount of times. I have explained before that an array is a variable that contains more variables. This for loop will go through all of the variables in the array returned by “bod.getFixtureList()” and run the code in the curly braces( the { and } braces ). This will get all the Fixtures that are attached to the body and set that fixture to be a sensor.

Let’s replace some of the code in our model so we can make a floor with some water and a circle to be our player.

We’ve commented out the createObject and createMovingObject as we’re no longer using them and we’ve completely removed the other bodies we were adding previously. We have then added a player who is a Box shaped body and some water which is also a box-shaped body. The water is then changed into a sensor with our makeAllFixturesSensors method. Run the program and see what happens.

Did you notice our player gets shot up in the air like it did previously? That is because our water body is a static body and we’ve still got our contact listener shooting up any bodies that hit a static body. How do we fix this? we can use the body’s setUserData method to set a unique name for our body which we will then check for in our contact listener.

After we define our water body add the following code to create a unique string to identify our water:

Now in our contact Listener add this code before the code that checks for static bodies to check if one of the fixtures belongs to the body with the user data “IAMTHESEA”.

Here we added the return statement. This tells your application that it is done processing the data and can return to whatever it wants to do next. So our code that checks for static bodies which come after this is skipped.

Now we will add what’s known as a flag. A flag is a boolean value in your code that is either on or off and is used to store the state of some object. We will be using it to store whether the player is in water or not. So in our B2dModel let’s add a field for our boolean value and call it isSwimming and set it to false so the player isn’t automatically swimming in the air.

We have made it public so that our contact listsner can change it when the player is in the water. We will also have to add some code to tell the model the player is our of the water and we will use the endcontact method for that.

Update your contact listener to match this code:

So now everytime our player is in our water sensor we tell the model they are swimming by setting isSwimming to true, and everytime they leave the sensor we tell the model that they are not swimming by setting isSwimming to false;

Now, all we need to do is to make the player act like they are swimming when in the water. We will do that in our B2dModel.

Replace the current logic method with this:

And change the player from a local variable to a field so we can access it in our logic method. Now when we run our code you will see that the player square starts falling but when it hits the water it slows down and starts to go back up until it completely the water and falls back in.

box2d swimming

Yay. We know have a basic understanding of our contact listener and we can change how a Box2D body reacts what it hits something or is in a sensor.

In part 6 we will go into using a controller to take input from the user and allow then to interact with our Box2D world.

Sharing is caring!

9 Replies to “Full LibGDX Game Tutorial – Box2D Contact Listener”

  1. Beautifull!

  2. Do you have the full source available for this part of the tutorial? At the end of the tutorial I don’t get the same behaviour as you’re describing. The “water” seems to be in the wrong place and the “player” hits the bottom and sky rockets off. I went over the tutorial multiple times, but I can’t figure out where the problem is.

    1. There is no source until part 7 which will contain this part’s source as well. So you could download that and check vs what you currently have.
      I will in the future run through all the tutorials again myself and create the source code for all parts and host of github.

    2. Hello Cain, I think that you have to apply more “force” for the object to swim. I was having the same problem, and I found out that the object “sinks” into the water. I solved it increasing the force applied to it:

      player.applyForceToCenter(0, 120, true);

      Also, make sure to comment the “create floor” line. It’s not explicitly stated in the tutorial but if you follow it, you may end up still having it “turned on”.

      createFloor(); –> comment this line!!!
      //createObject();
      //createMovingObject();

      Again thanks for the tutorial John (I’m the “Mucis” guy).

      1. Unfortunately that kind solution works only for a moment, if you will wait a bit longer time it (player body) will break through a water, anyway. I propose to add damping to body which has contact with water (see B2dContactListener class):
        @Override
        public void endContact(Contact contact) {
        System.out.print(“Lost Contact: “);
        Fixture fa = contact.getFixtureA();
        Fixture fb = contact.getFixtureB();
        if(fa.getBody().getUserData() == “IAMTHESEA”){
        fb.getBody().setLinearDamping(0.5f);
        if(Math.abs(fb.getBody().getLinearVelocity().y) < FLOATING_VMAX){
        fb.getBody().setLinearVelocity(0f,0f);
        }
        parent.isSwimming = false;
        return;
        }
        else if(fb.getBody().getUserData() == "IAMTHESEA"){
        fa.getBody().setLinearDamping(0.5f);
        if(Math.abs(fa.getBody().getLinearVelocity().y) < FLOATING_VMAX){
        fa.getBody().setLinearVelocity(0f, 0f);
        }
        parent.isSwimming = false;
        return;
        }
        }

        You should also add:
        private final float FLOATING_VMAX = 3f;

        I've also changed properties in 'Body Factory' class,
        'makeFixture' method:
        case 2: // RUBBER
        fixtureDef.density = 1.5f;
        fixtureDef.friction = 2f;
        fixtureDef.restitution = 0.9f;
        break;
        case 3:
        (…)
        case 4: // WATER
        fixtureDef.density = 1f;
        fixtureDef.friction = 0.77f;
        fixtureDef.restitution = 0.01f;

        and method in 'B2dModel' class:
        public void logicStep(float delta) {
        if(isSwimming){
        player.applyForceToCenter(0, 100, true);
        }
        world.step(delta, 6, 4);
        }

        and don't forget to change:
        // add some water
        water = bodyFactory.makeBoxPolyBody(1, -4, 40, 4,
        BodyFactory.WATER, BodyDef.BodyType.StaticBody);

        It still could be better (player body should to stay partially submerged in the water) in case where player's vertical velocity is close to zero.

  3. Awesome tutorial! Clean, well structured & very stable.

  4. On the off chance people are stupid like me and their listener isn’t reacting to the water:

    Make sure you are checking the fixture’s BODY ‘s user data and not the fixture itself’s user data.

    fa.getBody().getUserData()
    NOT
    fa.getUserData()

  5. […] further information have a look at this tutorial or the libGDX documentation on […]

  6. […] further information have a look at this tutorial or the libGDX documentation on […]

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.