Efficiently switching Screens (libGDX)

My main question is, what is your most efficient way of switching screens? I’m asking because I figured out a way however it feels hacky.

Right now I have a main class that extends Game, and two other classes that implement Screen, Screen1 and Screen2. My main class uses setScreen() in its create() method to change to Screen1. Screen1 has a Timer object in its show() method, and after a certain amount of time it triggers a boolean to true. That boolean is used in my main classes render() method to setScreen() over to Screen2.


class Main extends Game {
	public static boolean splashScreenTimer = false;

	@Override
	public void create () {
		setScreen(new SplashScreen(this));
	}

	@Override
	public void render () {
		super.render();

		if(splashScreenTimer) {
			splashScreenTimer = false;
			setScreen(new MainMenu(this));
		}
	}

         (...)
}


public class SplashScreen implements Screen {
    @Override
    public void show() {
       (...things happen...)

        Timer.schedule(new Timer.Task(){
                           @Override
                           public void run() {
                               Main.splashScreenTimer = true;
                               dispose();
                           }
                       }
                , FADE_TIME
        );
    }
}

My code gets the job done but it just feels like there is a more optimal way of handling switching between different screens.

I’m using a static class called “ScreenManager” that owns all screens and has a reference to the Game class.
The ScreenManager exposes static methods to change the screen.

public class ScreenManager {
	
	static Game game = null;
	static Screen loginScreen;
	static Screen gameScreen;

	
	private ScreenManager() {}
	
   //This is called by Game from inside the "create()" method.
	public static void initialize(Game game) {
		ScreenManager.game = game;

		loginScreen = new LoginScreen();
		gameScreen = new GameScreen();
	}
	
	public static void setLoginScreen() {
		game.setScreen(loginScreen);
	}

	public static void setGameScreen() {
		game.setScreen(gameScreen);
	}
}

You can initialize all screens on startup and keep them in memory if they aren’t huge.
Also, make sure that input-multiplexers are set correctly inside the “show()” function when switching screens, this can turn into an annoying hidden error.

Switching like this is very fast since all screens are initialized and ready to be used.

Thanks for sharing, that’s an interesting method of controlling your screens. I’m interested in how others ‘trigger’ their screens. In my example I have a splash screen that is an image within an actor – I’m using an actor so I have evoke actions on it such as fadeIn/fadeOut. My splash screen will live for X seconds, at which point I am switching to the main menu.

The best way I came up with handling this is assigning a Timer object within my splash screen. I feel this is dirty and was curious on how people generally handle switching screens.

From what I understand, we have show() and render() methods in our Screen classes. I’m not entirely sure on our options of breaking out of the render() method from one screen, in order to evoke another screen. Hence the hacky Timer object I’m using :slight_smile:

Oh, well you could add an action that switches the screen once the time is up, I’m using this for triggering screen changes with buttons like this:

Reusable click-listener:

public abstract class ClickListener implements EventListener {
	
	Actor actor;
	
	public ClickListener(Actor actor) {
		this.actor = actor;
	}
	
	@Override
	public boolean handle(Event event) {
		if(Gdx.input.justTouched()) {
			run();
			return true;
		}
		return false;
	}
	
	public abstract void run();
}

Reusable fade out action:

public class FadeOutAction extends Action {
	
	float alpha;
	float scl;
	
	public FadeOutAction(float time) {
		scl = 1f/time;
		alpha = 1f;
	}
	
	@Override
	public boolean act(float delta) {
		actor.getColor().a = alpha;
		alpha -= delta * scl;
		if(alpha <= 0) {
			run();
			return true;
		}
		return false;
	}
	
	public void run() {}
}

The fade-out action is added to the button once a click has been registered by he click listener and executes the run method after the actor is fully faded.

Added to a button with overridden run function:

		TextButton button0 = new TextButton("button0", skin);
		button0.addListener(new ClickListener(button0) {
			@Override
			public void run() {
				button0.addAction(new FadeOutAction(0.25f) {
					@Override
					public void run() {
						ScreenManager.setGameScreen();
					}
				});
			}
		});

You could instead add the action directly so that your intro plays and once it’s finished it triggers the screen change.

Thank you for your reply! I will use a similar effect later in my production. Upon further research I realized that what I was trying to do was too complicated.

All I needed to do was simply call gameclass.setScreen(currentGame), in order to switch screens. No need for booleans, no need for anything fancy. Going back to my original post all I needed to do was the following:

public class SplashScreen implements Screen {
    @Override
    public void show() {
       (...things happen...)

        Timer.schedule(new Timer.Task(){
                           @Override
                           public void run() {
                               GameClass.setScreen(currentGame);
                           }
                       }
                , FADE_TIME
        );
    }
}

Switching the screen will evoke the dispose() method, so make sure you dispose of everything you dont need when you switch screens.