:::: MENU ::::

How-to do heavy background processes on LibGDX, while showing a splash screen

  • Sep 18 / 2015
  • 0
Uncategorized

How-to do heavy background processes on LibGDX, while showing a splash screen

This article concerns fellow programmers that use libGDX. We hope you will find it useful.

I am sure every game developer has come to this point when your game runs smoothly after it is loaded but it takes some time to load and you want to make the user feel that nothing is wrong, your game is running and loading, but instead he sees a black screen or the app delays to launch. Well I had the same problem and I was looking at a black screen on my Android mid-cost device for three to four seconds before the logo screen of my game pops up.

I am using the libGDX Java game development framework and wanting to resolve this, my first approach was to implement it differently on every platform(desktop, android, iOS and html) but I quickly abandoned this plan, to try and manage to do it after loading the ApplicationListener, so every platform takes advantage of it the same way. At this point I should say that using a thread would probably be the best solution, but wanting my game to work in Html5/Gwt/Javascript, threads were not an option (more info here).
Anyway, I thought it would be a nice article to share and voilà:

So, we have a libGDX game that when loaded, it shows a menu screen (or in my case just before the menu screen, it shows a logo/splash screen timed for 1 sec and then moves to menu). The problem is that before seeing anything, you have a black screen on Android, and I guess on iOS you see the Default.png(did not check sorry). On Desktop, if your pc is slow, the app would delay to start, and on html you see the libGDX loading bar for a long time (by the way you can change this bar and the libGDX logo but this is not what we want right now).
WARNING: I think I saw some difference on the html bar showing for less time but I am not really sure it does get less. A flying spaghetti monster would know for sure 😛 , so if you are one of them or you can confirm it another way somehow, let me and everybody know.

I will be using the Game class and Screen interface along with scene2d package so if you are not familiar with those please, before continuing, take a quick look here and here.

We start with the the obvious. We will create a Screen implementation including a constant for the minimum milliseconds to show our Screen (this is our Logo Screen that will later show up while you are loading the game resources). We also include a declaration of the interface LogoEndCallback and a field variable of this type to use on our observer pattern and notify the Game class once we are done.

public class LogoScreen implements Screen {
	private static final int SHOW_DURATION_MILLIS = 1000;

	private LogoEndCallback logoTimeOutCallback;
	private Stage stage;
	private long startShowingTime;

	public interface LogoEndCallback{
		public void showAfterLogoScreen();
	}

	…………………………………………………..
	…………………………………………………..

}

We will need a Stage and a long variable marking the time our Logo started showing

	private Stage stage;
	private long startShowingTime;

And now our constructor. We will make sure we instantiate our stage, create our Image Actor with our logo(or anything you want to show on your splashscreen) and load it to the stage in the center of the Screen.

	public LogoScreen(LogoEndCallback logoTimeOutCallback) {
		this.logoTimeOutCallback = logoTimeOutCallback;
		stage = new Stage(v);

		Image logo = new Image(new TextureRegion(new Texture("logo.png")));
		stage.addActor(logo);
		logo.setPosition(
			(Gdx.graphics.getWidht()-logo .getWidth())/2,
			(Gdx.graphics.getHeight()-logo .getHeight())/2);		
	}

Now prepare your render method so that when the minimum time passes it runs the callback notifying your Game class

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

		//check if time has passed and notify your Game class
		if( (startShowingTime + SHOW_DURATION_MILLIS) <= System.currentTimeMillis() ) {
			logoTimeOutCallback.showAfterLogoScreen();
		}
	}

For the above code to work you need to set the startShowingTime variable and a good place to do it is the show method

	@Override
	public void show() {
		startShowingTime = System.currentTimeMillis();
	}

All good. Now to run this you have to create your Game Class and show your LogoScreen

public class MyGameApp extends Game {

	@Override
	public void create () {
		LogoEndCallback logoTimeOutCallback = new LogoEndCallback() {
			@Override
			public void showAfterLogoScreen() {
				//change your screen to your menu using
				// setScreen(menuScreen);
			}
		};

		LogoScreen logoScreen = new LogoScreen(logoTimeOutCallback);
		setScreen(logoScreen);
	}

	………………….
	//rest of Game class
	………………….
}

Till now you should have a logo/splash Screen that shows up for a certain time and then runs a callback provided to it's constructor. This does not load any heavy stuff but let's see how we are going to do this “background loading” without using a thread (because as I mentioned earlier, html does not support multithreading and I do not think it will in the future).

Go back to your logo screen and change your constructor arguments adding a Runnable and instantiating a SequenceAction that you have previously declared as a field. You will need another two booleans so let's declare them now.

	private SequenceAction loadingAction;
	private boolean appLoaded = false;
	private boolean loadingApp = false;

	public LogoScreen(LogoEndCallback logoTimeOutCallback,
			Runnable doOnBackground) {

		……………….
		//rest of constructor stuff here
		……………….

		loadingAction = Actions.sequence(
			Actions.run(doOnBackground),
			Actions.run(new Runnable() {

				@Override
				public void run() {
					appLoaded = true;
				}
			}));
	}

You could easily add this action to your stage but this would make it run before the call to the draw method and it is not what you want, so you will add the Action to your stage at the end of the render method, after the call to the draw, using an if and the second boolean variable as below.

	@Override
	public void render(float delta) {
		……………….
		// rest of render stuff
		……………….

		if( !loadingApp ){
			stage.addAction(loadingAction);
			loadingApp = true;
		}
	}

Now make your LogoScreen wait for your action to finish before you call the callback, by also checking the appLoaded variable.

		if( (startShowingTime + SHOW_DURATION_MILLIS) <= System.currentTimeMillis()
				&& appLoaded ) {
			logoTimeOutCallback.showAfterLogoScreen();
		}

Last but most important of all, go to your Game create() method and when instantiating the LogoScreen pass a Runnable argument that will contain all the heavy stuff you want to do, in it's run() method

	@Override
	public void create () {
		………………………….
		//rest of create method
		………………………….

		Runnable doOnBackground = new Runnable(){
			@Override
			public void run() {
				//do all your heavy processes here
				//it will be executed while your LogoScreen is showing
			};
		};

		LogoScreen logoScreen = new LogoScreen(logoTimeOutCallback, 					doOnBackground);
		setScreen(logoScreen);
	}

That's it! I think explaining takes much longer than actually writing the code.
Of cource you could simplify the code more using lamda expresions with Java 8 but unfortunately Java 8 is not yet supported by Android for sure, I think by roboVM/iOS and I really have no clue about GWT/html.
I hope this works for you as it worked for me. Please leave your comments below.

Happy Coding!
Klitos Giannakopoulos
Add me on Google+,
Follow me on Twitter

Leave a comment

You must be logged in to post a comment.