[Solved] I'm having an issue with my ContactListener not setting a screen

Hello everyone,

First post here, so I’d like to extend my appreciation to the community. I’ve been lurking a bit and you guys are very helpful!

My problem is with setting a ContactListener on a Border class I’ve created that registers when something has hit it. After three collisions, I would like it to go to a Game Over screen. Here are the important bits of my code. I hope someone can show me what I’m missing, so I can patch one more hole in the mystery of the LibGDX and Box2D combo. Of course, other methods of implementation are welcome as well. :wink:

DatGame.java


public class DatGame extends Game {
	
	World world;
	OrthographicCamera camera;
	Border border;
	
	Box2DDebugRenderer renderer;
	
	Body body;
	
	GameOver gameOver;
	
	@Override
	public void create() {		
		
		gameOver = new GameOver(this);
		
		world = new World(new Vector2(0, -9.81f), true);
		camera = new OrthographicCamera(28, 20);
		border = new Border(world, camera.viewportWidth, camera.viewportHeight, this);
		
		renderer = new Box2DDebugRenderer();
		
		PolygonShape square = new PolygonShape();
		square.setAsBox(2, 2);
		
		BodyDef bodyDef = new BodyDef();
		bodyDef.type = BodyType.DynamicBody;
		bodyDef.position.set(new Vector2(0,0));
		
		FixtureDef fixtureDef = new FixtureDef();
		fixtureDef.shape = square;
		fixtureDef.density = 1;
		fixtureDef.friction = .4f;
		fixtureDef.restitution = .8f;
		
		body = world.createBody(bodyDef);
		Fixture fixture = body.createFixture(fixtureDef);
		fixture.setUserData("Square");
	
		world.setContactListener(border);
		
	}
	
	@Override
	public void render() {		
		super.render();
		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		
		body.applyAngularImpulse(100, true);

		world.step(1/60f, 6, 3);
		
		renderer.render(world, camera.combined);
	}
}

Border.java


public class Border implements ContactListener {
	
	BodyDef bodyDef;
	FixtureDef fixtureDef;
	PolygonShape plank;
	
	DatGame game;

	Sound ding;
	float tones[] = {1f, 12*(float)(Math.log10(.84)/Math.log10(2)) - 1, 12*(float)(Math.log10(.667)/Math.log10(2)) - 1, 12*(float)(Math.log10(.561)/Math.log10(2)) - 1, 2f};
	int lives = 3;
	
	public Border(World world, float borderWidth, float borderHeight, DatGame game) {
		
		this.game = game;
		
		// top
		createPlank(world, borderWidth, 0, 0, borderHeight / 2);
		// bottom
		createPlank(world, borderWidth, 0, 0, -borderHeight / 2);
		// left
		createPlank(world, 0, borderHeight, -borderWidth / 2, 0);
		// right
		createPlank(world, 0, borderHeight, borderWidth / 2, 0);
	}
	
	public void createPlank(World world, float plankWidth, float plankHeight, float borderWidth, float borderHeight) {
		
		ding = Gdx.audio.newSound(Gdx.files.internal("data/ding.wav"));
		
		plank = new PolygonShape();
		plank.setAsBox(plankWidth, plankHeight);
		
		bodyDef = new BodyDef();
		bodyDef.type = BodyType.StaticBody;
		bodyDef.position.set(borderWidth, borderHeight);
		
		fixtureDef = new FixtureDef();
		fixtureDef.shape = plank;
		
		world.createBody(bodyDef).createFixture(fixtureDef).setUserData("Border");
	}

	@Override
	public void beginContact(Contact contact) {
		Fixture fixtureB = contact.getFixtureB();
		if (fixtureB.getUserData().equals("Square")) {
			ding.setPitch(ding.play(), tones[MathUtils.random(0,4)]);
			lives--;
		}
		if (lives == 0) {
			Gdx.app.log("Border", "Game Over!");
			game.setScreen(game.gameOver);
		}
	}
}

GameOver.java


public class GameOver implements Screen {
	
	DatGame game;
	
	Sprite sprite;
	SpriteBatch batch;
	
	public GameOver (DatGame game) {
		this.game = game;
	}
	
	@Override
	public void show() {
		sprite = new Sprite(new Texture("data/libgdx.png"));
		batch = new SpriteBatch();
	}

	@Override
	public void render(float delta) {
		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		batch.begin();
		sprite.draw(batch);
		batch.end();
	}
}

DatGame (awesome name) is not a Screen, it’s the game itself, so it always renders no matter which Screen is currently set.
In the render method of DatGame, you call super.render() which calls the render method of the currently set Screen.
When GameOver becomes the currently set Screen, super.render() calls the render method of GameOver.
It draws everything, but then you clear the screen and draw over it.

super.render(); // calls GameOver.render(delta)
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // erases everything that GameOver has rendered
// ...
renderer.render(world, camera.combined); // draws the old things on the screen again

You should take everything in DatGame and move it into a seperate new Screen (let’s call it Start for example).
Then, DatGame should look like this:

public class DatGame extends Game {

	@Override
	public void render() {
		super.render(); // render the current Screen
	}

	@Override
	public void create() {
		setScreen(new Start()); // set the old stuff as current Screen
	}

	@Override
	public void resize(int width, int height) {
		super.resize(width, height); // call the resize method of the current screen
	}

	@Override
	public void pause() {
		super.pause(); // call the pause method of the current Screen
	}

	@Override
	public void resume() {
		super.resume(); // call the resume method of the current Screen
	}

	@Override
	public void dispose() {
		super.dispose(); // call the dispose method of the current Screen
	}

}

Like this, you just call the super method in every method, which always calls the corresponding method of the currently set Screen.
It will stop drawing the old things over the GameOver screen every frame because super.render() just calls the render method of the currently set Screen and nothing comes after super.render().

Wow, thanks! This did it and it works great now.