2012-02-16

#2 - libgdx Tutorial: Game Screens

Libgdx version used on this post: 0.9.2 (download)
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:
  1. Go to the OpenTyrian project page
  2. Download the latest Win32 OpenTyrian release build
  3. Download Tyrian 2.1
  4. Unpack both archives in the same directory
  5. Run opentyrian.exe
Great game, isn't it? Nostalgia aside, let's plan our screen flow.


Notes on the screen flow:
  • 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:
public class Tyrian
    extends
        Game
{
    public SplashScreen getSplashScreen()
    {
        return new SplashScreen( this );
    }

    @Override
    public void create()
    {
        Gdx.app.log( Tyrian.LOG, "Creating game" );
        fpsLogger = new FPSLogger();
        setScreen( getSplashScreen() );
    }

    (...)
}
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.
public class Tyrian
    extends
        Game
{
    @Override
    public void render()
    {
        super.render();

        // output the current FPS
        fpsLogger.log();
    }

    (...)
}
public abstract class AbstractScreen
    implements
        Screen
{
    @Override
    public void render(
        float delta )
    {
        // the following code clears the screen with the given RGB color (black)
        Gdx.gl.glClearColor( 0f, 0f, 0f, 1f );
        Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT );
    }

    (...)
}

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:
package com.blogspot.steigert.tyrian.screens;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.blogspot.steigert.tyrian.Tyrian;

public class SplashScreen
    extends
        AbstractScreen
{
    private Texture splashTexture;
    private TextureRegion splashTextureRegion;

    public SplashScreen(
        Tyrian game )
    {
        super( game );
    }

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

        // load the splash image and create the texture region
        splashTexture = new Texture( "splash.png" );

        // we set the linear texture filter to improve the stretching
        splashTexture.setFilter( TextureFilter.Linear, TextureFilter.Linear );

        // in the image atlas, our splash image begins at (0,0) at the
        // upper-left corner and has a dimension of 512x301
        splashTextureRegion = new TextureRegion( splashTexture, 0, 0, 512, 301 );
    }

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

        // we use the SpriteBatch to draw 2D textures (it is defined in our base
        // class: AbstractScreen)
        batch.begin();

        // we tell the batch to draw the region starting at (0,0) of the
        // lower-left corner with the size of the screen
        batch.draw( splashTextureRegion, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight() );

        // the end method does the drawing
        batch.end();
    }

    @Override
    public void dispose()
    {
        super.dispose();
        splashTexture.dispose();
    }
}
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.

You can check-out/browse the resulting code on the Google Code web-site.
Thanks for reading!

46 comments:

  1. Bookmarked your blog! Waiting for your next article :)

    ReplyDelete
  2. 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.

    ReplyDelete
    Replies
    1. 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.

      For each post I create a tag on the source code tree. Here you have a list of each post and the associated tag: https://code.google.com/p/steigert-libgdx/

      Delete
  3. Hi Great tutorial. how would you get the splash screen to auto switch to the mainmenu after a set time?]
    Best

    Daniel

    ReplyDelete
    Replies
    1. Hi Daniel,
      Please have a look at the post #3 where we use scene2d actions to fade the splash screen out and switch to the main menu screen automatically.
      Thanks for reading!

      Delete
    2. Thanks Gustavo, all makes sense now :)

      Delete
  4. Great tutorial. My images appear upside-down, however. Any idea how to fix this?

    ReplyDelete
    Replies
    1. Maybe you're using a negative height?
      If not, I suggest you to post a message on the libgdx forum: http://www.badlogicgames.com/forum/

      Delete
  5. This is one of the best tutorial ive seen about libgdx,pls keep up the good work and keem em coming :)

    ReplyDelete
    Replies
    1. I second this. After 20 searches from google, finally had a good tutorial.

      Delete
  6. First of all great tutorial.
    Secondly I have a question, in the docs it states that

    A SpriteBatch is a pretty heavy object so you should only ever have one in your program.

    But you use a new one for each Screen, so I was wondering if this will effect the overall gaming experience ?

    ReplyDelete
    Replies
    1. Thanks!

      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.

      Delete
  7. Hi Gustavo,

    This is alittle off topic for this page, but do you have an example or tutorial on AssetManager? especially the part regarding the resource identifier.

    Thanks

    Daniel

    ReplyDelete
    Replies
    1. To be honest I didn't know this AssetManager class. Maybe this post can help you: http://www.badlogicgames.com/forum/viewtopic.php?f=11&t=3497&p=17103&hilit=assetmanager#p17103

      Thanks for reading!

      Delete
    2. Hi , Thanks for the reply!

      I have tried this example as to loading but it is throwing a TWLaunch error on my tablet.. which i not good..so kinda looking for another which explains another approach to implementing it.

      Thanks though.
      Cheers

      Daniel

      Delete
  8. hi , Thanks for this tutorial,
    you wrote AbstractScreen class with render method ,, what about other methods ,, e.g. constructor , show() , ..
    can you write their implementation please

    ReplyDelete
    Replies
    1. Hi Zainab! Please check out the full source code at: https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/AbstractScreen.java

      Thanks for reading!

      Delete
  9. This comment has been removed by the author.

    ReplyDelete
  10. 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?

    ReplyDelete
    Replies
    1. 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.

      Delete
  11. Hi,

    Do you have a tutorial on how to do scoring in a game?

    thanks

    Daniel

    ReplyDelete
    Replies
    1. Not yet! I'll write one eventually.

      Delete
  12. Hi,

    Thanks a ton for this tutorial.

    I am facing an issue regarding loading of splash.png. When I run desktop launcher it throws error as:

    Caused by: java.io.IOException: couldn't load pixmap

    Please advise.

    Manish

    ReplyDelete
  13. Hi! I answered you in the first post:
    http://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.html?showComment=1342634181075#c4366688675746026053

    ReplyDelete
  14. nice blog but i have a small doubt,

    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?

    ReplyDelete
  15. This is a great tutorial! Could do a new one or explain the differences to use OpenGL ES 2.0 to splash an image that doesn't need to be a square and power of 2? Thanks!

    ReplyDelete
    Replies
    1. I'm using OpenGL 2.0, I resized the image to 512x301, and the only difference I found so far is that with OpenGL 2.0 you don't need the TextureRegion. This is my splashscreen render method:

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

      batch.begin();

      batch.draw( splashTexture, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight() );

      batch.end();
      }

      Delete
  16. Thank you very much for these tutorials. May I ask how you figured these out and how long it took you? Did you just read through the java docs and experiment a bit?

    Also, I'd be interested in an answer to Jason Pollmann's question, as well.

    ReplyDelete
  17. 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.

    ReplyDelete
  18. 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.

    ReplyDelete
  19. 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

    txtr = new Texture( Gdx.files.internal( "splash.png" ));
    splashImage = new Image( txtr );
    splashImage.setFillParent( false );

    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.

    Any ideas?

    ReplyDelete
    Replies
    1. Of course I played with
      splashImage.setFillParent( false );
      and
      splashImage.setFillParent( true );

      Delete
  20. 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.

    ReplyDelete
  21. Great libGDX series .........
    in code give below,i got two errors.
    In Tyrian.java class,



    public SplashScreen getSplashScreen()
    {
    return new SplashScreen(this);
    }
    @Override
    public void create() {
    // TODO Auto-generated method stub

    Gdx.app.log(Tyrian.LOG, "Creating Games");
    fpslogger = new FPSLogger();
    setScreen(getSplashScreen());
    }


    1) return new SplashScreen(this); ------> The constructor SplashScreen(Tyrian) is undefined

    2) setScreen(getSplashScreen()); --------> The method setScreen(Screen) in the type Tyrian is not applicable for the arguments (SplashScreen)

    Please help me...

    ReplyDelete
  22. hi.
    i import your project in my eclipse but it have some erro
    "java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.blogsport.steigert.tyrian/com.blogsport.steigert.tyrian.TyrianAndroidLauncher}: java.lang.ClassNotFoundException: com.blogsport.steigert.tyrian.TyrianAndroidLauncher"

    ReplyDelete
  23. 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.

    ReplyDelete
  24. I know that this is only a tutorial and example but: constructor in abstract class? seriously?
    Great tutorial anyway, but maybe change it because people will learn how to make huge mistakes.

    ReplyDelete
    Replies
    1. I can't see why this is a problem. Please share with us your thoughts..

      Delete
    2. 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 :)

      Delete
    3. Well, you can create contructor in abstract class but better option would be creating a base class that extends it.
      But this topic have so many answers as many programmer is in World, so... :)

      As I said before, great tutorial. Easy to read and understand :)

      Delete
    4. 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:

      1) Spring Framework's AbstractBeanDefinition (this is like one of the core classes of this great framework)
      2) JRE Swing's AbstractAction
      3) Google's AbstractCursor in Android
      4) Apache's AbstractRegion in Commons-Math

      I could give you thousands of examples... But as you said, each one of us have different "progamming cultures". Thanks for your post!

      Delete
    5. Always ready to learn and hear about other opinions :)
      Thanks for the examples, now I can start some discussion with professors at my University ;]

      Delete
  25. Know India is an Educational App that helps you know about India. This is my first game in LibGDX using Scene2D ...... plz download and give ur reviews....

    https://play.google.com/store/apps/details?id=com.jaansi.knowindia

    ReplyDelete
  26. 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...

    I have become very much Confused..

    ReplyDelete
  27. Just 3 words: Great tutorial, sir :)

    ReplyDelete