This code is what I use, it provides a simple way to do everything. Explanation is below the code.
Base classes:
CustomGame.java
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.utils.ObjectMap;
/**
* @author Ivan Vinski
* @since 1.0
*/
public abstract class CustomGame implements ApplicationListener {
public enum State {
ALL_GOOD, NOT_INITIALIZED, TO_RESET
}
private ObjectMap<Class<?>, CustomScreen> screens = new ObjectMap<Class<?>, CustomScreen>();
private ObjectMap<Class<?>, State> states = new ObjectMap<Class<?>, State>();
private CustomScreen screen;
public final boolean registerScreen(CustomScreen screen) {
Class<?> clazz = screen.getClass();
if (screens.containsKey(clazz)) {
return false;
}
screens.put(clazz, screen);
states.put(clazz, State.NOT_INITIALIZED);
return true;
}
@Override
public void render() {
Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
if (screen != null) {
screen.render(Gdx.graphics.getDeltaTime());
}
}
@Override
public void resize(int width, int height) {
if (screen != null) {
screen.resize(width, height);
}
}
@Override
public void pause() {
if (screen != null) {
screen.pause();
}
}
@Override
public void resume() {
if (screen != null) {
screen.resume();
}
}
@Override
public void dispose() {
for (CustomScreen screen : screens.values()) {
State state = states.get(screen.getClass());
if (state != State.NOT_INITIALIZED) {
screen.dispose();
}
}
}
public CustomScreen getScreen() {
return screen;
}
public <T extends CustomScreen> void setScreen(Class<T> clazz) {
if (screen != null) {
screen.hide();
Gdx.input.setInputProcessor(null);
}
screen = screens.get(clazz);
if (screen != null) {
State state = states.get(clazz);
if (state == State.NOT_INITIALIZED) {
screen.init(this);
states.put(clazz, State.ALL_GOOD);
} else if (state == State.TO_RESET) {
screen.dispose();
screen.init(this);
states.put(clazz, State.ALL_GOOD);
}
screen.show();
Gdx.input.setInputProcessor(screen.input());
}
}
public <T extends CustomScreen> void resetScreen(Class<T> clazz) {
if (screens.containsKey(clazz)) {
states.put(clazz, State.TO_RESET);
}
}
public static CustomGame getInstance() {
return getInstance(CustomGame.class);
}
public static <T extends CustomGame> T getInstance(Class<T> castTo) {
return (T) Gdx.app.getApplicationListener();
}
}
CustomScreen.java
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Screen;
/**
* @author Ivan Vinski
* @since 1.0
*/
public interface CustomScreen extends Screen {
void init(CustomGame game);
InputProcessor input();
}
Implementations:
Implementation of CustomGame.java
/**
* @author Ivan Vinski
* @since 1.0
*/
public class MyGame extends CustomGame {
@Override
public void create() {
registerScreen(new ScreenA());
registerScreen(new ScreenB());
setScreen(ScreenA.class);
}
}
Implementation of CustomScreen.java
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
/**
* @author Ivan Vinski
* @since 1.0
*/
public class ScreenA extends InputAdapter implements CustomScreen {
private Batch batch;
private OrthographicCamera camera;
private Texture texture;
private float x, speed;
@Override
public void init(CustomGame game) {
batch = new SpriteBatch();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(camera.viewportWidth / 2f, camera.viewportHeight / 2f, 0f);
texture = new Texture(Gdx.files.internal("badlogic.jpg"));
speed = 10f;
}
@Override
public void show() {
x = 0f;
}
@Override
public void render(float delta) {
camera.update();
x += speed * delta;
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(texture, x, 0);
batch.end();
}
@Override
public void hide() {
speed *= 2;
}
@Override
public void dispose() {
batch.dispose();
texture.dispose();
}
@Override
public InputProcessor input() {
return this;
}
@Override
public boolean keyDown(int keycode) {
if (keycode == Input.Keys.ESCAPE) {
MyGame.getInstance().setScreen(ScreenB.class);
return true;
}
return super.keyDown(keycode);
}
@Override
public void resize(int width, int height) {
}
@Override
public void pause() {
}
@Override
public void resume() {
}
}
Explanation:
As you see in the examples (implementations), you need to call registerScreen(CustomScreen)
method to be able to setScreen(Class<T extends CustomScreen>)
to that screen. You can always call getInstance()
to access the CustomGame easily or you can call getInstance(Class<T extends CustomGame>)
to automatically cast it to your implementation of CustomGame meaning that you can access any non-static variables easily.
setScreen(Class<T extends CustomScreen>)
method takes care of everything depending on the state it’s in and it automatically sets the input to the InputProcessor
specified in screen’s input()
method. You can easily reset any screen by calling resetScreen(Class<T extends CustomScreen>)
, but it needs to be called before setScreen(Class<T extends CustomScreen>)
method since that method determines should it initialize or reset screen.
Also, screen is cleared automatically before calling render(float)
method and all screens are disposed automatically if they have been initialized.