2012-02-16

#2 - libgdx Tutorial: Game Screens

On the previous post we created our project structure on Eclipse, talked about the events handled by our game and created some launchers for desktop and Android. The next step is to plan the game screens we'll have and come up with a solution to implement them inside our main game project (tyrian-game).

More about Tyrian

Tyrian was released in 1995 by Epic Games. In 2004 it was made a freeware, and since 2007 all its graphics are available under an open license. I suggest you play the game to have a feel about it. The steps follow:

A splash screen is initially shown, then we move to the main menu screen.

The menu allows the player to start playing the game, look the hall of fame and adjust some options.

The start-game screen allows the player to start a new game or resume a previous saved game.

The profile screen allows the player to view his/her profile and manage the ship.

The level screen loads a level and allows the player to play it.

Implementing the screens

It would be nice to separate the code based on the screen. In order to achieve that in libgdx, we could modify our game's main class (Tyrian) to extend com.badlogic.gdx.Game, which in turn implements ApplicationListener. Don't forget to modify your event handling methods to call super, now that we're exetending a class rather than implementing an interface. Doing that, we'll have support to use the com.badlogic.gdx.Screen interface, which delegates the event handling methods to one specific screen at a time. Creating a base Screen class might come handy, so the final picture looks like this:

I'm not proud of that circular dependency between Tyrian and the screens, but for the sake of a tutorial I'll ignore this problem. The Tyrian class will also be in charge of creating Screens and enabling them (remember: always one screen each time). Each Screen instance will be in charge of rendering itself. This is a nice approach because each screen has a limited scope, which facilitates the maintenance of the code. To complete this step we should tell our game to initially show the splash screen, and that is a piece of cake:

Now that we have a base Screen class (AbstractScreen), I'll do some refactoring. The render() method of Tyrian will just call super.render() and output the FPS, and the render() method of AbstractScreen will clear the screen with the color black.

The delta parameter on the Screen's render method

Did you notice that "delta" parameter? In case you're wondering its meaning, let's imagine two scenarios. On the first scenario, the player has a very humble smartphone, which can process only 10 frames per second (FPS) of our game. On the second scenario, a player has a high-end quad core smartphone capable of processing 100 frames per second. In order to define the same notion of time on both devices, you use the "delta" parameter in your mathematical calculations. That ensures the high-end device of our second scenario won't display the game 10 times faster than the first device, but just output more frames to have a smoother game experience. We won't use that by now, but as it showed up I wanted to give a heads up.

Adding an image to the splash screen

It would be really sad if by the second post of this tutorial all we had was an empty screen. Browsing the web I found a very cool image we could use for our splash screen. Check it out:

Sadly we cannot just throw that image on our resources folder the way it is. Libgdx (and many other gaming frameworks) requires us to use images with dimensions in the power of 2 (that is: 256x256, 512x256, 1024x512 and so on). But there is more to it. This image can contain more than one image, so what you really have is an image atlas. Later we tell which part of this image atlas we want to draw.

Edit: there is a tool called TexturePacker that eases the job of creating image atlases. It will be detailed in a future article.

I want the splash image to stretch to take all the space available. So I must ensure the splash image's ratio has the same ratio of our game's window, which is 1.6 (remember that our game window's dimension is set to 800x480, so 800/480 gives us 1.666666666666667). Using the GIMP image editor I worked the image out to make its ratio 1.6 and came up with the following image atlas of 512x512:

So far so good. The next step is to throw this image atlas in our resources folder, which is: tyrian-android/assets. As the resources folder of tyrian-game is a link to this folder, Eclipse automatically shows this image under the tyrian-game project tree. Finally, we modify our SplashScreen class to use this resource, as follows:

When we execute the desktop launcher, the following window is displayed:

Conclusions

We improved our main game project to support screens, each with its own well-defined scope. We refactored the code a bit and changed the SplashScreen to actually show something. And we realized that working with images is not that easy, but believe me, it could be much harder. Libgdx does most of the work under the covers, so our screens can focus on their jobs.

47 comments:

I am trying to follow your tutorial but I am experiencing some difficulty. For instance in the SplashScreen class's constructor I call super(game). But where is that being called? The AbstractScreen does not have a constructor that takes a Game object, in my case a TwistedRubber object.

Hi Abdullah, first of all thanks for the feedback. You're correct. The code I pasted here is incomplete. But you're better off using the source code browser in my Google Code project. For instance, you could see the full source code for the AbstractScreen class at the time I published this post.

I've seen the source code of other libgdx games to write this post, and the approach they use is quite the same. As screen switching does not generaly occur too often, I'd say there is no concerns regarding performance.

Notice that in Tyrian the SpriteBatch instance is lazily created when needed, reused if requested again, and disposed when the screen hides.

In your tutorials, you have used an abstract class as well as a tyrian game class. This is different from how super jumper(a demo in libgdx) works, in which mario directly implemented the screen class...can the splash screen also be implemented in that way?

It's up to your ApplicationListener's implementation to decide what to render. You could use no screen class and show a splash screen inside ApplicationListener#render, for instance, but doing so our code would be all cluttered and difficult to maintain.

In Mario's Super Jumper a custom Game class is provided, but it acts almost the same as the Game class we extend in our Tyrian class.

lets say i have an assets class and im rendering my graphics via a call to the assets.loadassets(), so in that situation,where should i write the " xyzTexture.setFilter( TextureFilter.Linear, TextureFilter.Linear ); " , in the assets class? or the class which renders the graphics?

Hi ,Thanks for the awesome tutorial. I am facing an issue in this lesson. The image displays well in the desktop version. But on my android device (Acer Iconia Tab ) , it still shows a green screen .I have tried but couldn't figure out anything . Please advise.

At first i want to thank u for this wonderful tutorial. I am having a problem with "The constructor SplashScreen(Tyrian) is undefined" and "The method setScreen(Screen) in the type Game is not applicable for the arguments (SplashScreen)" in Tyrian.java file. Please help me.

I am just going crazy...I do not understand this whole 2 square thing, I mean how to use it correctly. I made a splash screen using the code given above and a menu screen as well. Everything works fine except the positioning and aligning of the splash image.Instead of using an image atlas I just use

The screen resolution is 480x800 (portrait) so is the splash image. I tried to use 512x512 and 1024x1024 modified (extended) images made from the original 480x800 splash image. I tried to move the image to the middle and then to the top-left corner within the extended image (at 1024x1024). I never get the correct splash screen, I get clipped image or a narrow image with a white right side (that is the color of the extension when in 1024x1024).I do not know what to do.

hi, i have just started learning Libgdx and game programming, i am following u . that too nice tutorial . but i just wants to point out that in SplashScreen class there is no Batch variable is defined. so please guide regarding that.

Thank you so very much, your blogs are very helpful. I am a beginner with LibGDX, and am developing a virtual chemistry lab. The project requires frequent switching of screens. The screens switch well using setScreen() method, but the problem is that the project takes up a HUGE amount of RAM. Three screens take up about 345Mbs within a minute of execution.I have ensured disposing of every texture,stage,batch using screen's dispose method. I explicitly called it in Hide() too. Please help, otherwise such a project would be useless.

Abstract classes shouldn't have constructor. It's like basics of programming. As its name says it's ABSTRACT. You can't find it, you can't create an object like that, because it's just abstract :) You can only extend it to create something. Using a constructor in abstract class is a very wrong writing pattern.

For example: humanoid is abstract class. you can't find humanoid itself in real life. You can find Homo Sapiens species, in particular Me, that extends Humanoid class. You can find me, you can create any other human, but you can't create something as abstract like humanoid :)

I understand your opinion, but I continue to disagree. The fact I'm using a constructor in an abstract class doesn't mean it's supposed to be called by the users of this class' instances. It's supposed to be used by the classes that extend it. I can use this "trick" to avoid duplicating lines of code in the extending classes.

It's just a matter of letting the abstract class deal with its own state, and this can be done through the use of constructors and methods.

I have more than 10 years of professional experience with Java. In this time I've seen abstract classes being used this way with no problem at all. Let me give you some real world examples:

Hello Sir in this code I am really confused because in the previous Tuts we were implementing Application Listener in out main class and now in this tut that implementation is gone and also that in my program the code given below which you have shown (public SplashScreen getSplashScreen() { return new SplashScreen( this ); }

)I am experiencing the problem in the return part.

Also is it not true that when we make a class abstract then when we are using that class in other class then we need to use Implement...