[libGDX] [box2D] How to use Contact Listener?

I have done a test but I receipt a NullPointerException. I don’t now why receipt this, so, I have the contact listener in a class and in the Screen class I put this method: (and connect it to the create method)


public void testContact(){
		BodyDef bdtest = new BodyDef();
		bdtest.type = BodyType.StaticBody;
		bdtest.position.set(new Vector2(5, 5));
		
		btest = world.createBody(bdtest);
		
		CircleShape cs = new CircleShape();
		cs.setRadius(0.5f);
		
		ftest = btest.createFixture(cs, 0.6f);
		
		//NullPointerExeption
		test = new Test(instance, player.fplayer, ftest);
		
		world.setContactListener(test);
	}

This is the NullPointerException:


java.lang.NullPointerException
	at com.unclain.udevelop.prueba1.ScreenTestOne.testContact(ScreenTestOne.java:301)
	at com.unclain.udevelop.prueba1.ScreenTestOne.show(ScreenTestOne.java:125)
	at com.badlogic.gdx.Game.setScreen(Game.java:62)
	at com.unclain.udevelop.prueba1.GameScreen$1.onEvent(GameScreen.java:71)
	at aurelienribon.tweenengine.BaseTween.callCallback(BaseTween.java:380)
	at aurelienribon.tweenengine.BaseTween.updateStep(BaseTween.java:521)
	at aurelienribon.tweenengine.BaseTween.update(BaseTween.java:424)
	at aurelienribon.tweenengine.TweenManager.update(TweenManager.java:166)
	at com.unclain.udevelop.prueba1.GameScreen.render(GameScreen.java:37)
	at com.badlogic.gdx.Game.render(Game.java:46)
	at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:187)
	at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:110)

This is the ContactListener class:


package com.unclain.udevelop.prueba1.contacts;

import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.ContactImpulse;
import com.badlogic.gdx.physics.box2d.ContactListener;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Manifold;
import com.unclain.udevelop.prueba1.GameScreen;
import com.unclain.udevelop.prueba1.ScreenTestOne;
import com.unclain.udevelop.prueba1.UDevelop1;
import com.unclain.udevelop.prueba1.ingame.EntityPlayer;

public class Test implements ContactListener{

	private Fixture fA;
	private Fixture fB;
	private ScreenTestOne mainInstance;

	public Test(ScreenTestOne mainInstance, Fixture fA, Fixture fB){
		this.mainInstance = mainInstance;
		this.fA = fA;
		this.fB = fB;
		
		mainInstance = new ScreenTestOne(new GameScreen(new UDevelop1()));
	}
	
	@Override
	public void beginContact(Contact contact) {
		// TODO Auto-generated method stub
		fA = contact.getFixtureA();
		fB = contact.getFixtureB();
		
		if(fA.getBody() != null && fB.getBody() != null){
			if(fA.equals(mainInstance.player.fplayer) && fB.equals(mainInstance.ftest)){
				System.out.println("¡QUESO!");
			}
		}
	}

	@Override
	public void endContact(Contact contact) {
		// TODO Auto-generated method stub
		fA = contact.getFixtureA();
		fB = contact.getFixtureB();
		
		if(fA.getBody() != null && fB.getBody() != null){
			if(fA.equals(mainInstance.player.fplayer) && fB.equals(mainInstance.ftest)){
				System.out.println("¡QUESO!");
			}
		}
	}

	@Override
	public void preSolve(Contact contact, Manifold oldManifold) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void postSolve(Contact contact, ContactImpulse impulse) {
		// TODO Auto-generated method stub
		
	}

}

I was searching but I don’t find nothing useful, so, How work ContactListener? How can I detect a contact between two Fixtures?

Thanks.

P.D: I’m spanish, so, thanks for try understand me. ;D

Have you made sure that instance and fplayer aren’t null? Show us the code where they’re initialized.

Here’s my Screen class:


package com.unclain.udevelop.prueba1;

import box2dLight.PointLight;
import box2dLight.RayHandler;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL11;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.unclain.udevelop.prueba1.contacts.Test;
import com.unclain.udevelop.prueba1.ingame.EntityPlayer;
import com.unclain.udevelop.prueba1.utils.CharacterController;
import com.unclain.udevelop.prueba1.utils.ContactCheck;
import com.unclain.udevelop.prueba1.utils.MovingPlatform;
import com.unclain.udevelop.prueba1.utils.SpriteAnimation;

public class ScreenTestOne implements Screen {
	
	OrthographicCamera cam;
	Array<MovingPlatform> platforms = new Array<MovingPlatform>();
	MovingPlatform groundedPlatform = null;
	BitmapFont font;
	
	public GameScreen gamescreen;
	
	public World world;
	
	public OrthographicCamera camera;
	
	private Box2DDebugRenderer renderer;
	
	private RayHandler rayHandler;

	private ContactCheck contactCheck;
	
	private CharacterController playerController;
		
	private SpriteBatch spriteBatch;
	
	private SpriteAnimation spriteAnimation;
	
	public EntityPlayer player;
	
	private PointLight plr;
	
	public Fixture ftest;
	public Body btest;
	
	public Test test;
	
	public ScreenTestOne instance = this;
	
	public static final float WORLD_TO_BOX=0.01f;
	public static final float BOX_WORLD_TO=100f;
	public float ConvertToBox(float x){
	    return x*WORLD_TO_BOX;
	}
	
	private Texture tplayer;
	private static final int FILAS = 4;
	private static final int COLUMNAS = 4;
	
	public ScreenTestOne(GameScreen gamescreen){
		this.gamescreen = gamescreen;
	}
	
	@Override
	public void render(float delta) {
		// TODO Auto-generated method stub
		Gdx.gl.glClear(GL11.GL_COLOR_BUFFER_BIT);
		Gdx.gl.glClearColor(0, 0.5f, 0.1f, 1);
		world.step(delta, 6, 6);
		
		renderer.render(world, camera.combined);
		
		rayHandler.updateAndRender();
		rayHandler.setCombinedMatrix(camera.combined);
		
		camera.position.set(playerController.bodyPlayer.getPosition().x, playerController.bodyPlayer.getPosition().y, 0);
		camera.update();
		
		contactCheck.update(delta);
		
		playerController.addKeyControls();
		
		for(int i = 0; i < platforms.size; i++) {
			MovingPlatform platform = platforms.get(i);
			platform.update(Math.max(1/30.0f, Gdx.graphics.getDeltaTime()));
		}
		
		playerController.drawFont(spriteBatch);
		
		spriteAnimation.draw(spriteBatch, ConvertToBox(player.bplayer.getPosition().x), ConvertToBox(player.bplayer.getPosition().y), delta);
		
		camControl();
	}

	@Override
	public void resize(int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void show() {
		// TODO Auto-generated method stub
		world = new World(new Vector2(0, -10), true);
		camera = new OrthographicCamera(28, 20);
		playerAnimation();
		testContact();
		crearJugador();
		
		renderer = new Box2DDebugRenderer();
		
		rayHandler = new RayHandler(world);
		
		contactCheck = new ContactCheck(world, player.bplayer, player.fplayer);
		
		playerController = new CharacterController(world, player.bplayer, player.fplayer, player.fdplayer, camera);
		
		plr = new PointLight(rayHandler, 5000, Color.RED, 200, 0, 50);
		new PointLight(rayHandler, 5000, Color.BLUE, 200, 100, 50);
		createWorld();
		
		spriteBatch = new SpriteBatch();
	}
	
	private void createWorld() {
		float y1 = 1; //(float)Math.random() * 0.1f + 1;
		float y2 = y1;
		for(int i = 0; i < 50; i++) {
			Body ground = createEdge(world, BodyType.StaticBody, -50 + i * 2, y1, -50 + i * 2 + 2, y2, 0);			
			y1 = y2;
			y2 = 1; //(float)Math.random() + 1;
		}	
 
		Body box = createBox(BodyType.StaticBody, 1, 1, 0);
		box.setTransform(30, 3, 0);
		box = createBox(BodyType.StaticBody, 1.2f, 1.2f, 0);
		box.setTransform(5, 2.4f, 0);				
 
		/*platforms.add(new MovingPlatform(world, -2, 3, 2, 0.5f, 2, 0, 4));
		platforms.add(new MovingPlatform(world, 17, 3, 5, 0.5f, 0, 2, 5));		
		platforms.add(new MovingPlatform(world, -7, 5, 2, 0.5f, -2, 2, 8));
		platforms.add(new MovingPlatform(world, -10, 10, 4, 1f, -1, 3, 5));*/
	}
	
	   public void camControl(){
		   int control = 0;
		   if(player.bplayer.getPosition().y >= 40){
			   control = 3;
			   camera.zoom = control;
			   camera.update();
		   } else if(player.bplayer.getPosition().y == 30){
			   control = 2;
			   camera.zoom = control;
			   camera.update();
		   } else if(player.bplayer.getPosition().y == 20){
			   control = 1;
			   camera.zoom = control;
			   camera.update();
		   } else if(player.bplayer.getPosition().y <= 10){
			   control = 1;
			   camera.zoom = control;
			   camera.update();
		   }
		   
		   
		   if(Gdx.input.isKeyPressed(Keys.E)){
			   camera.zoom = 3;
			   camera.update();
		   }
	   }
	   
	
	private Body createEdge(World world, BodyType type, float x1, float y1, float x2, float y2, float density) {
		this.world = world;
		BodyDef def = new BodyDef();
		def.type = type;
		Body box = world.createBody(def);
 
		PolygonShape poly = new PolygonShape();		
		poly.setAsBox(x2 - x1, y2 - y1);//(new Vector2(0, 0), new Vector2(x2 - x1, y2 - y1));
		box.createFixture(poly, density);
		box.setTransform(x1, y1, 0);
		poly.dispose();
 
		return box;
	}
 
	private Body createCircle(BodyType type, float radius, float density) {
		BodyDef def = new BodyDef();
		def.type = type;
		Body box = world.createBody(def);
 
		CircleShape poly = new CircleShape();
		poly.setRadius(radius);
		box.createFixture(poly, density);
		poly.dispose();
 
		return box;
	}
	
	private Body createBox(BodyType type, float width, float height, float density) {
		BodyDef def = new BodyDef();
		def.type = type;
		Body box = world.createBody(def);
 
		PolygonShape poly = new PolygonShape();
		poly.setAsBox(width, height);
		box.createFixture(poly, density);
		poly.dispose();
 
		return box;
	}

	@Override
	public void hide() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void pause() {
		// TODO Auto-generated method stub

	}

	@Override
	public void resume() {
		// TODO Auto-generated method stub

	}

	@Override
	public void dispose() {
		// TODO Auto-generated method stub
		world.dispose();
		renderer.dispose();
		rayHandler.dispose();
	}

	public void crearSuelo(){
		BodyDef bdsuelo = new BodyDef();
		bdsuelo.type = BodyType.StaticBody;
		bdsuelo.position.set(new Vector2(0, -50));
		
		Body bsuelo = world.createBody(bdsuelo);
		
		PolygonShape ps = new PolygonShape();
		ps.setAsBox(Gdx.graphics.getWidth(), 2f);
		
		Fixture fsuelo = bsuelo.createFixture(ps, 0.5f);
		
		ps.dispose();
	}
	
	public Body crearJugador(){
		player = new EntityPlayer(0, 40, 10, 15f, world);
		Vector2 vel = player.getVelocity();
		Vector2 pos = new Vector2(0, -30);
		player.setVelocity(vel);
		player.setPosition(pos);
				
		return player.bplayer;
	}
	
	public void playerAnimation(){
		tplayer = new Texture(Gdx.files.internal("spritesheet_caveman.png"));
		spriteAnimation = new SpriteAnimation(tplayer, FILAS, COLUMNAS);
	}
	
	public void testContact(){
		BodyDef bdtest = new BodyDef();
		bdtest.type = BodyType.StaticBody;
		bdtest.position.set(new Vector2(5, 5));
		
		btest = world.createBody(bdtest);
		
		CircleShape cs = new CircleShape();
		cs.setRadius(0.5f);
		
		ftest = btest.createFixture(cs, 0.6f);
		
		//NullPointerExeption
		test = new Test(instance, player.fplayer, ftest);
		
		world.setContactListener(test);
	}
}

And here the FixtureA class


package com.unclain.udevelop.prueba1.utils;

import java.util.List;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.FPSLogger;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Contact;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.WorldManifold;

public class CharacterController {
   
   private boolean jump = false;
   private boolean grounded = false;
   
   private final static float MAX_VELOCITY = 7f;
   
   float stillTime = 0;
   long lastGroundTime = 0;
   
   public Body bodyPlayer;
   private FixtureDef fixtureDefPlayer;
   private Fixture fixturePlayer;
   private MovingPlatform groundedPlatform = null;
   
   private ContactCheck checker;
   
   private World world;
   
   private FPSLogger fps;
   
   private BitmapFont font;
   
   private OrthographicCamera camera;
   
   public CharacterController(World world, Body bodyPlayer,
		   Fixture fixturePlayer, FixtureDef fixtureDefPlayer,
		   OrthographicCamera camera){
      this.bodyPlayer = bodyPlayer;
      this.fixtureDefPlayer = fixtureDefPlayer;
      this.fixturePlayer = fixturePlayer;
      this.world = world;
      this.camera = camera;
      addKeyControls();
      fps = new FPSLogger();
      System.out.println("Jump = " + jump);
      checker = new ContactCheck(world, bodyPlayer, fixturePlayer);
      font = new BitmapFont();
   }
   
   public void addKeyControls(){
	   Vector2 vel = bodyPlayer.getLinearVelocity();
	   Vector2 pos = bodyPlayer.getPosition();
	   
	   grounded = isGrounded(Gdx.graphics.getDeltaTime());

	   if(grounded){
		   lastGroundTime = System.nanoTime();
	   } else {
		   if(System.nanoTime() - lastGroundTime < 100000000){
			   grounded = true;
		   }
	   } 
	   
	   if(grounded && stillTime > 0.1){
		   
	   }
	   
	   if(Math.abs(vel.x) > MAX_VELOCITY){
		   vel.x = Math.signum(vel.x) * MAX_VELOCITY;
		   bodyPlayer.setLinearVelocity(vel.x, vel.y);
	   }
	   
	   if(!Gdx.input.isKeyPressed(Keys.A) && !Gdx.input.isKeyPressed(Keys.D)) {			
			stillTime += Gdx.graphics.getDeltaTime();
			bodyPlayer.setLinearVelocity(vel.x * 0.9f, vel.y);
		}
		else { 
			stillTime = 0;
		}
	   
	   if(!grounded) {			
			fixturePlayer.setFriction(100f);
		} else {
			if(!Gdx.input.isKeyPressed(Keys.A) && !Gdx.input.isKeyPressed(Keys.D) && stillTime > 0.2) {
				fixturePlayer.setFriction(100f);
			}
			else {
				fixturePlayer.setFriction(0.2f);
			}

			if(groundedPlatform != null && groundedPlatform.dist == 0) {
				bodyPlayer.applyLinearImpulse(0, -24, pos.x, pos.y);				
			}
		}
	   
	   if(Gdx.input.isKeyPressed(Keys.A) && vel.x > -MAX_VELOCITY) {
			bodyPlayer.applyLinearImpulse(-2f, 0, pos.x, pos.y);
		}
	   
	   if(Gdx.input.isKeyPressed(Keys.D) && vel.x < MAX_VELOCITY) {
			bodyPlayer.applyLinearImpulse(2f, 0, pos.x, pos.y);
	   }
	   
	   if(Gdx.input.isKeyPressed(Keys.T)){
		   bodyPlayer.applyLinearImpulse(0, 3f, pos.x, pos.y);
	   }
	   
	   if(Gdx.input.isKeyPressed(Keys.W)){
		   jump = true;
	   }
	   
	   if(jump) {			
			jump = false;
			if(grounded == true) {
				bodyPlayer.setLinearVelocity(vel.x, 0);			
				System.out.println("jump before: " + bodyPlayer.getLinearVelocity());
				bodyPlayer.setTransform(pos.x, pos.y + 0.01f, 0);
				bodyPlayer.applyLinearImpulse(0, 10, pos.x, pos.y);			
				System.out.println("jump, " + bodyPlayer.getLinearVelocity());				
			}
	   }
   }
   
   public boolean isGrounded(float deltaTime) {				
		groundedPlatform = null;
		List<Contact> contactList = world.getContactList();
		for(int i = 0; i < contactList.size(); i++) {
			Contact contact = contactList.get(i);
			if(contact.isTouching() && (contact.getFixtureA() == fixturePlayer)) {				

				Vector2 pos = bodyPlayer.getPosition();
				WorldManifold manifold = contact.getWorldManifold();
				boolean below = true;
				for(int j = 0; j < manifold.getNumberOfContactPoints(); j++) {
					below &= (manifold.getPoints()[j].y < pos.y - 1.5f);
				}

				if(below) {
					if(contact.getFixtureA().getUserData() != null && contact.getFixtureA().getUserData().equals("p")) {
						groundedPlatform = (MovingPlatform)contact.getFixtureA().getBody().getUserData();							
					}

					if(contact.getFixtureB().getUserData() != null && contact.getFixtureB().getUserData().equals("p")) {
						groundedPlatform = (MovingPlatform)contact.getFixtureB().getBody().getUserData();
					}											
					return true;
				}

				return true;
			}
		}
		return false;
	}
   
   public void drawFont(SpriteBatch batch){
	   bodyPlayer.setAwake(true);
	   batch.begin();
	   font.drawMultiLine(batch, "friction: " + fixturePlayer.getFriction() + "\ngrounded: " + grounded + "\njump: " + 
	   jump + "\nFPS: " + Gdx.graphics.getFramesPerSecond() + "\nrestitution: " + fixtureDefPlayer.restitution +
	   "\ndensity: " + fixtureDefPlayer.density + "\nawake: " + bodyPlayer.isAwake() + "\nstilltime: " + stillTime + 
	   "\nJugador.x " + bodyPlayer.getPosition().x + "\nJugador.y " + bodyPlayer.getPosition().y +
	   "\nZoom " + camera.zoom, 15, Gdx.graphics.getHeight() - 15);
	   batch.end();
   }

}


P.D: Yes, class do not are totally good, it have things with no use. Actually I’m correcting all pass by pass.