libGDX: Часть 6.1. Работа с ContactListener из Box2D

Box2D Test

В Box2D есть специальный интерфейсный класс ContactListener, реализовав который, можно обрабатывать коллизии. В этой статье покажу как его использовать на практических примерах.

ContactListener — интерфейс, который можно реализовать в своём классе для дальнейшего использования в игровом мире. Необходимо реализовать 4 метода: beginContact, endContact, preSolve, postSolve. Наш класс будет иметь примерно такой вид:

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){
    	  
      }

}

Чтобы использовать, необходимо назначить его игровому миру.

World world = new World(new Vector2(0, -20), true);
world.setContactListener(new MyContactListener());

beginContact

Срабатывает, когда два объекта начинают накладываться. Прокает только в рамках шага.

endContact

Срабатывает, когда два объекта прекращают соприкасаться. Может быть вызван, когда тело разрушено, таким образом, это событие может иметь место вне временного шага.

preSolve

Срабатывает после обнаружения столкновения, но перед его обработкой. Это позволяет нам как-то изменить контакт до его обработки. Например, можно сделать контакт неактивным. Допустим, вы хотите сделать так, чтобы персонаж проходил сквозь движущуюся платформу. Тогда preSolve будет выглядеть так:

     @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.setEnabled(false) вызывать постоянно.

contact.getFixtureA().getUserData().equals("p") используется для идентификации объекта. Напомню, что при создании платформы используется метод platform.getFixtureList().get(0).setUserData("p");.

postSolve

Метод позволяет осуществить логику игры, которая изменяет физику после контакта. Например, деформировать или уничтожить объект после контакта. Однако, Box2D не позволяет вам изменять физику в методе, потому что вы могли бы разрушить объекты, которые Box2D в настоящее время обрабатывает, приводя к ошибке.

Есть тут одна тонкость — нельзя просто удалить объект, так как он может обрабатываться где-то в данный момент, и в итоге вы получите ошибку: 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); 		  
    	  }
      }

Теперь, при столкновении блоки, для которых задано getFixtureList().get(0).setUserData("b") будут уничтожены. Я писал сверху, что при обычном удалении будет ошибка. Но, если перед удалением сделать объект неактивным body.setActive(false), то ошибки не будет.

Исходники

Можете скачать исходники Libgdxtutorial-lesson6.1.rar. Они довольно сыроваты, правда. Но понять принципы работы помогут.