Working with ContactListener from Box2d in Libgdx

  • Tutorial
In continuation of the last article about using Box2d in Libgdx, I decided to consider working with the class ContactListener.

From the name of the class, it is obvious that it should be used to handle collisions. Consider a couple of practical examples.

image


ContactListener- An interface that can be implemented in its class for future use in the game world.

It is necessary to implement 4 methods: beginContact, endContact, preSolve, postSolve.

Our class will look something like this:
public class MyContactListener implements ContactListener{
      @Override 
      public void endContact(Contact contact) {
      }
      @Override
      public void beginContact(Contact contact) {
      }
      @Override
      public void preSolve (Contact contact, Manifold oldManifold){
      }
      @Override
      public void postSolve (Contact contact, ContactImpulse impulse){
      }
}


To use it, you must assign it to the game world.
world.setContactListener(new MyContactListener());


beginContact

Fires when two objects begin to overlap. It only works as part of a step.

endContact

Fires when two objects stop touching. It can be triggered when the body is destroyed, so this event can take place outside the time step.

preSolve

It works after a collision is detected, but before it is processed. This allows us to somehow change the contact before processing it. For example, you can make a contact inactive. Take an example from a previous article. We used a moving platform. Suppose you want to make the character go through it. Then it preSolvewill look like this:
     @Override
      public void preSolve (Contact contact, Manifold oldManifold){
    	  WorldManifold manifold = contact.getWorldManifold();
    	  for(int j = 0; j < manifold.getNumberOfContactPoints(); j++){
    		  if(contact.getFixtureA().getUserData() != null && contact.getFixtureA().getUserData().equals("p"))
    			  contact.setEnabled(false);
    		  if(contact.getFixtureB().getUserData() != null && contact.getFixtureB().getUserData().equals("p"))
    			  contact.setEnabled(false);
    	  }
      }


contact.getFixtureA().getUserData().equals("p")used to identify an object. Let me remind you that when creating the platform, the method is used platform.getFixtureList().get(0).setUserData("p").


postSolve

The method allows you to implement the logic of the game, which changes the physics after contact. For example, deform or destroy an object after contact. However, Box2D does not allow you to change the physics in the method, because you could destroy the objects that Box2D is currently processing, resulting in an error.

There is one subtlety here - you cannot just delete an object, since it can be processed somewhere at the moment, and as a result you will get an error: And so, in the method we will delete the blocks that we encountered.
java.lang.NullPointerException
at com.badlogic.gdx.physics.box2d.World.contactFilter




@Override
      public void postSolve (Contact contact, ContactImpulse impulse){
    	  Body body = null;
    	  if(contact.getFixtureA() != null && contact.getFixtureA().getUserData() != null  && contact.getFixtureA().getUserData().equals("b"))
    		  body = contact.getFixtureA().getBody();
    	  if(contact.getFixtureB() != null && contact.getFixtureB().getUserData() != null  && contact.getFixtureB().getUserData().equals("b"))
    		  body = contact.getFixtureB().getBody();
    	  if(body != null){  		 
    		 body.setActive(false);
    		 world.destroyBody(body); 		  
    	  }
      }


Now, in a collision, the blocks for which the set getFixtureList().get(0).setUserData("b")will be destroyed. I wrote from above that there will be an error with a normal deletion. But, if you make the object inactive before deleting body.setActive(false), then there will be no error.


Source code

You can download the source from the blog . They are pretty damp, really. But to understand the principles of work will help.

Also popular now: