Tutorial libgdx

download Tutorial libgdx

of 47

Transcript of Tutorial libgdx

  • 8/10/2019 Tutorial libgdx

    1/47

    IntroductionLibgdx version used on this post: 0.9.2 (download)

    Developing games is a very difficult task, even if you want something simple. There aremany technical concepts to grasp and then you must learn how to implement themcorrectly and efficiently on your game's target platform. This is the first post of a tutorial thatI'll write about libgdx - an open source high-performance Java framework for developinggames. A very cool feature of libgdx is that your game can run on your desktop computer(and that's not using the emulator), which eases the testing and debugging process. Thenyou can make it run on an Android device just by writing one line of code. You can evendistribute your games as an applet or via Webstart if you want so. Some features of libgdxare implemented using native code (like physics and audio processing functions), whichoptimizes its execution at runtime.

    So libgdx seams to be a nice gaming framework, but what does it provide? I'll highlight itsmain features: An API for OpenGL ES (texture, shaders, vertex arrays and so on) An API for 2D graphics (bitmap fonts, sprites, UI widgets, animations and so on) An API for 3D graphics (OBJ and MD5 loaders, camera manipulation and so on) An API for I/O (audio, files, graphics, key/touch/click events and so on) An API for physics (box2d) and math Many utilities (JSON, bitmap font generator, texture packer and so on)

    OpenGL ES is a royalty-free, cross-platform API for full-function 2D and 3D graphics onembedded systems - including consoles, phones, appliances and vehicles. It consists ofwell-defined subsets of desktop OpenGL, creating a flexible and powerful low-levelinterface between software and graphics acceleration. OpenGL ES includes profiles forfloating-point and fixed-point systems and the EGL specification for portably binding to

    native windowing systems. OpenGL ES 1.X is for fixed function hardware and offersacceleration, image quality and performance. OpenGL ES 2.X enables full programmable3D graphics.

    The OpenGL ES 1.0 and 1.1 API specifications have been supported since Android 1.0.Beginning with Android 2.2 (API Level 8), the framework supports the OpenGL ES 2.0 APIspecification.

    Installing libgdx

    I'm considering you've already installed Java, Eclipse and theAndroid SDK,and everythingis working perfectly. Now is the time to download libgdx. I'm using the latest versionavailable for now: 0.9.2. Now, instead of going through all the installation steps involved, Iwant you to watchthis videoon YouTube. The guy on the video is Mario Zechner, writer ofthe Beginning Android Gamesbook and the main programmer of libgdx. He'll guide youthrough the basic steps to setup some projects on Eclipse that use the libgdx framework.

    Setting up the projects for our game

    We'll implement a game together likeTyrian.I used to play this game a lot when I was akid, so I'm really looling forward to what we'll achieve. Of course that our final result will be

    much simpler than the actual game, but that will do to learn how a game is created usinglibgdx. The game will improve as I write new posts for the tutorial. I'll put all the source code

    http://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://www.libgdx.com/http://www.libgdx.com/http://developer.android.com/sdk/installing.htmlhttp://developer.android.com/sdk/installing.htmlhttp://developer.android.com/sdk/installing.htmlhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://www.youtube.com/watch?v=vLx_72qxK_0&feature=player_embeddedhttp://www.youtube.com/watch?v=vLx_72qxK_0&feature=player_embeddedhttp://www.youtube.com/watch?v=vLx_72qxK_0&feature=player_embeddedhttp://www.amazon.com/Beginning-Android-Games-Mario-Zechner/dp/1430230428http://www.amazon.com/Beginning-Android-Games-Mario-Zechner/dp/1430230428http://www.amazon.com/Beginning-Android-Games-Mario-Zechner/dp/1430230428http://www.youtube.com/watch?v=tQdu4NsWU4Yhttp://www.youtube.com/watch?v=tQdu4NsWU4Yhttp://www.youtube.com/watch?v=tQdu4NsWU4Yhttp://www.youtube.com/watch?v=tQdu4NsWU4Yhttp://www.amazon.com/Beginning-Android-Games-Mario-Zechner/dp/1430230428http://www.youtube.com/watch?v=vLx_72qxK_0&feature=player_embeddedhttp://code.google.com/p/libgdx/downloads/listhttp://developer.android.com/sdk/installing.htmlhttp://www.libgdx.com/http://code.google.com/p/libgdx/downloads/list
  • 8/10/2019 Tutorial libgdx

    2/47

    onGoogle Code,so you can easily check it out using a Subversion client, or just browse itwith your browser.Okay, so let's start off creating our projects. I followed Zechner's instructions on the videoand came up with the following project structure on Eclipse:

    The tyrian-android project depends on the tyrian-game project. This dependency isconfigured in the "Java Build Path > Projects" section of the tyrian-android project'ssettings. But it isn't enough. On the tab "Libraries" you should manually add the "gdx.jar" of

    tyrian-game. Should you skip this step, the game won't run on an Android device (althoughit will compile without errors). With this project structure created, you can run the tyrian-game on your desktop computer to develop it, and sometimes run the tyrian-androidproject on your Android smartphone/tablet to check if everything is going well.

    Where to put resource files?

    Eventually we'll need to add audio and image files to our game. These resources should beaccessible by both our projects. Instead of duplicating the resources, there is a cool trickwe can do in Eclipse. Note that our Android project has an "assets" folder. By the time wehave some resources, we should put them in this folder. In order to share these resources

    with the game project, we create a "linked" source folder. Open the game project's settingsand click the "Link Source..." button as follows:

    The game's main class

    http://code.google.com/p/steigert-libgdx/http://code.google.com/p/steigert-libgdx/http://code.google.com/p/steigert-libgdx/http://3.bp.blogspot.com/-Q_r5baJLNgE/TzwnxGrRN2I/AAAAAAAAA8I/JG--NNeiPwc/s1600/assets-trick.pnghttp://4.bp.blogspot.com/-dBrQ3uDcEB4/Tzw-8UCr7_I/AAAAAAAAA8g/8Jt1PHkS-3o/s1600/libgdxTutorialProjectStructure+(1).pnghttp://code.google.com/p/steigert-libgdx/
  • 8/10/2019 Tutorial libgdx

    3/47

    At the simplest form, a game's main class is an instance ofcom.badlogic.gdx.ApplicationListenerin libgdx. You should get used to read the Javadocsof libgdx's classes, because many details won't be covered here. Tip: the source code forthe libgdx's JARs can be attached to our projects.

    Your game's main class is responsible for handling specific application events, like "create",

    "render", "dispose" and so on. The events fired on the desktop don't have exactly the samebehavior when the game is executed on Android, but they're quite similar. The following listshows the application lifecycle events that can be fired, and when it happens: create- Fired once when the application is created. resize- Fired every time the game screen is re-sized and the game is not in thepaused state. It is also fired once just after the create event. render - Fired by the game loop from the application every time the renderinghappens. The game update should take place before the actual rendering. pause- Fired just before the application is destroyed. On Android it happens whenthe home button is pressed or an incoming call is happening. On desktop it's fired justbefore disposal when exiting the application. This a good time to save the game state onAndroid as it is not guaranteed to be resumed.

    resume- Fired ONLY on Android, when the application receives the focus. dispose - Fired when the application is being destroyed. It is preceded by thepause event.

    In order to capture and handle these events, you implement the methods ofthecom.badlogic.gdx.ApplicationListener interface. You should also know that when libgdxcreates our game, a separate thread called Main loop thread is created and an OpenGLcontext is attached to it. All event processing executes within this separate thread, and notwithin the UI thread.

    Enough reading. Let's create our game's main class. Please have a look at the followingcode and read the inline comments.

    ?12345678910111213141516

    17181920212223242526272829303132

    333435

    package com.blogspot.steigert.tyrian;

    import com.badlogic.gdx.ApplicationListener;import com.badlogic.gdx.Gdx;import com.badlogic.gdx.graphics.FPSLogger;import com.badlogic.gdx.graphics.GL20;

    /*** The game's main class, called as application events are fired.*/

    public class Tyrianimplements

    ApplicationListener{

    // constant useful for loggingpublic static final String LOG = Tyrian.class.getSimpleName();

    // a libgdx helper class that logs the current FPS each secondprivate FPSLogger fpsLogger;

    @Overridepublic void create(){

    Gdx.app.log( Tyrian.LOG, "Creating game" );fpsLogger = new FPSLogger();

    }

    @Overridepublic void resize(

    int width,int height )

    {

    Gdx.app.log( Tyrian.LOG, "Resizing game to: " + width + " x " + height );}

    http://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.htmlhttp://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.htmlhttp://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.html
  • 8/10/2019 Tutorial libgdx

    4/47

    3637383940414243

    44454647484950515253545556575859

    6061626364

    @Overridepublic void render(){

    // the following code clears the screen with the given RGB color (green)Gdx.gl.glClearColor( 0f, 1f, 0f, 1f );Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT );

    // output the current FPS

    fpsLogger.log();}

    @Overridepublic void pause(){

    Gdx.app.log( Tyrian.LOG, "Pausing game" );}

    @Overridepublic void resume(){

    Gdx.app.log( Tyrian.LOG, "Resuming game" );}

    @Override

    public void dispose(){Gdx.app.log( Tyrian.LOG, "Disposing game" );

    }}

    Notes on the above code: As stated previously, our game's main class is just an instanceofcom.badlogic.gdx.ApplicationListener. With the FPSLoggerclass it gets easy to output the current FPS each second, sowe have a feel about our game's performance. The Gdxclass holds singleton instances of many modules: graphics, audio, filesand so on. By calling the Gdx.app.log() method you can log messages in a cross-platform

    manner. The render()method just clears the screen with the color green.

    Running our game

    If we're able to run our game on different platforms, we need one specific launcher for eachone. Let's start with the desktop launcher. Please have a look at the following code andread the inline comments.?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    packagecom.blogspot.steigert.tyrian;

    importcom.badlogic.gdx.ApplicationListener; importcom.badlogic.gdx.backends.lwjgl.LwjglApplication;

    /*** This class simply creates a desktop LWJGL application.*/

    publicclassTyrianDesktopLauncher {

    publicstaticvoidmain(String[] args )

    {// create the listener that will receive the application events

    ApplicationListener listener = newTyrian();

    // define the window's title

    http://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.htmlhttp://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.htmlhttp://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.html
  • 8/10/2019 Tutorial libgdx

    5/47

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    String title = "Tyrian";

    // define the window's sizeintwidth = 800, height = 480;

    // whether to use OpenGL ES 2.0

    booleanuseOpenGLES2 = false;

    // create the gamenewLwjglApplication( listener, title, width, height, useOpenGLES2 );

    }}

    That's all. If you run this class, you should be able to see the following result:

    And for the Android devices, you can create an activity like this:?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    packagecom.blogsport.steigert.tyrian;

    importandroid.os.Bundle;

    importcom.badlogic.gdx.backends.android.AndroidApplication; importcom.blogspot.steigert.tyrian.Tyrian;

    /*** This class simply defines an Android activity for our game. */

    publicclassTyrianAndroidLauncher extends

    AndroidApplication {

    @OverridepublicvoidonCreate(

    Bundle savedInstanceState ){

    super.onCreate( savedInstanceState );

    // whether to use OpenGL ES 2.0

    booleanuseOpenGLES2 = false;

    http://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.htmlhttp://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.htmlhttp://3.bp.blogspot.com/-BJTGkmm1OIg/Tzw3nntQpDI/AAAAAAAAA8Q/w-zP471_0I4/s1600/desktop.pnghttp://steigert.blogspot.com.br/2012/02/1-libgdx-tutorial-introduction.html
  • 8/10/2019 Tutorial libgdx

    6/47

    21

    22

    23

    24

    25

    26

    27

    // create the gameinitialize( newTyrian(), useOpenGLES2 );

    }}

    And when you execute it on an Android device you'll get:

    Conclusion

    We created the project structure for our game and tested it both on the desktop and on theAndroid device. The next posts will focus on libgdx internals and how to use it to implementthe features of our cool game. A project was created on Google Code so that you caneasily browse the source code. I created atag on the Subversion repositoryfor this post's

    project state. Let me know if I can improve/correct something, and I see you soon!

    Game ScreensLibgdx version used on this post: 0.9.2 (download)

    On the previous post we created our project structure on Eclipse, talked about the eventshandled by our game and created some launchers for desktop and Android. The next stepis to plan the game screens we'll have and come up with a solution to implement theminside 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 since2007 all its graphics are available under an open license. I suggest you play the game tohave a feel about it. The steps follow:

    1. Go to the OpenTyrian project page2. Download the latest Win32 OpenTyrian release build3. Download Tyrian 2.14. Unpack both archives in the same directory5. Run opentyrian.exe

    Great game, isn't it? Nostalgia aside, let's plan our screen flow.

    https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120215https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120215https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120215http://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/opentyrian/http://code.google.com/p/opentyrian/http://4.bp.blogspot.com/-iQVEhYRUu_w/Tzw5B32wd_I/AAAAAAAAA8Y/jRvjjxVUvs0/s1600/device.pnghttp://code.google.com/p/opentyrian/http://code.google.com/p/libgdx/downloads/listhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120215
  • 8/10/2019 Tutorial libgdx

    7/47

    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 andadjust some options. The start-game screen allows the player to start a new game or resume a previoussaved 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 inlibgdx, 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 handlingmethods to call super, now that we're extending a class rather than implementing aninterface. 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 abase Screenclass might come handy, so the final picture looks like this:

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

    2

    3

    4

    5

    6

    7

    publicclassTyrianextends

    Game{

    publicSplashScreen getSplashScreen()

    { returnnewSplashScreen( this);}

    http://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://4.bp.blogspot.com/-ed-9WL8O7OU/Tz0zK9tFlZI/AAAAAAAAA84/WYCImTyN3WQ/s1600/libgdxTutorialScreenClassDiagram.pnghttp://1.bp.blogspot.com/-gaPHPYAYLXE/Tz0sW7nedlI/AAAAAAAAA8s/-pkqeBadzEc/s1600/libgdxTutorialGameScreens.pnghttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.html
  • 8/10/2019 Tutorial libgdx

    8/47

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    @Overridepublicvoidcreate(){

    Gdx.app.log( Tyrian.LOG, "Creating game");fpsLogger = newFPSLogger();

    setScreen( getSplashScreen() );}

    (...)}

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

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    publicclassTyrianextends

    Game{

    @Overridepublicvoidrender(){

    super.render();

    // output the current FPSfpsLogger.log();

    }

    (...)}

    ?1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    publicabstractclassAbstractScreenimplements

    Screen{

    @Overridepublicvoidrender(

    floatdelta ){

    // 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 imaginetwo 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 playerhas a high-end quad core smartphone capable of processing 100 frames per second. In

    http://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.html
  • 8/10/2019 Tutorial libgdx

    9/47

    order to define the same notion of time on both devices, you use the "delta" parameter inyour mathematical calculations. That ensures the high-end device of our second scenariowon't display the game 10 times faster than the first device, but just output more frames tohave a smoother game experience. We won't use that by now, but as it showed up Iwanted 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 itout:

    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 of2 (that is: 256x256, 512x256, 1024x512 and so on). But there is more to it. This image cancontain more than one image, so what you really have is an image atlas. Later we tellwhich part of this image atlas we want to draw.Edit:there is a tool called TexturePacker that eases the job of creating image atlases. Itwill be detailed in a future article.I want the splash image to stretch to take all the space available. So I must ensure thesplash image's ratio has the same ratio of our game's window, which is 1.6 (remember thatour game window's dimension is set to 800x480, so 800/480 gives us1.666666666666667). Using the GIMP image editor I worked the image out to make itsratio 1.6 and came up with the following image atlas of 512x512:

    http://code.google.com/p/libgdx/wiki/TexturePackerhttp://code.google.com/p/libgdx/wiki/TexturePackerhttp://www.gimp.org/http://www.gimp.org/http://www.gimp.org/http://4.bp.blogspot.com/-bbD30wrjV88/Tz1EiikOgNI/AAAAAAAAA9E/xEmGEDKuZjw/s1600/TSHPSP%257E1.GIFhttp://www.gimp.org/http://code.google.com/p/libgdx/wiki/TexturePacker
  • 8/10/2019 Tutorial libgdx

    10/47

    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, wemodify our SplashScreenclass to use this resource, as follows:?1

    2

    3

    4

    5

    67

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    3334

    35

    packagecom.blogspot.steigert.tyrian.screens;

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

    publicclassSplashScreenextends

    AbstractScreen{

    privateTexture splashTexture;privateTextureRegion splashTextureRegion;

    publicSplashScreen(Tyrian game )

    {super( game );

    }

    @Overridepublicvoidshow(){

    super.show();

    // load the splash image and create the texture regionsplashTexture = newTexture( "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 = newTextureRegion( splashTexture, 0, 0, 512, 301);}

    http://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.htmlhttp://1.bp.blogspot.com/-UScvUA4VXlQ/Tz1HdWbsdeI/AAAAAAAAA9Q/S6aCEjkUa1g/s1600/splash.pnghttp://steigert.blogspot.com.br/2012/02/2-libgdx-tutorial-game-screens.html
  • 8/10/2019 Tutorial libgdx

    11/47

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    5657

    58

    59

    60

    61

    62

    @Overridepublicvoidrender(

    floatdelta ){

    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 screenbatch.draw( splashTextureRegion, 0, 0, Gdx.graphics.getWidth(),

    Gdx.graphics.getHeight() );

    // the end method does the drawingbatch.end();

    }

    @Overridepublicvoiddispose()

    {

    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-definedscope. We refactored the code a bit and changed the SplashScreen to actually showsomething. And we realized that working with images is not that easy, but believe me, itcould be much harder. Libgdx does most of the work under the covers, so our screens canfocus on their jobs.You can check-out/browse the resulting code on theGoogle Code web-site.Thanks for reading!

    scene2dLibgdx version used on this post: 0.9.2 (download)

    https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120216https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120216https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120216http://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://2.bp.blogspot.com/-uUNhiqpSEew/Tz1MW8UzVoI/AAAAAAAAA9c/piZ7zLfo0SI/s1600/Untitled.pnghttp://code.google.com/p/libgdx/downloads/listhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120216
  • 8/10/2019 Tutorial libgdx

    12/47

    All we have by now is a splash screen that displays an static image. Wouldn't it be nice toadd some fading effect on this image? Things like these improve the user experience a lot,so soon we'll deal with that. As we build our graphical user interface, we need to handlethings like selecting options, clicking on buttons, giving focus to an input widget and so onso forth. Can you imagine doing that with images inside image atlases? You're lucky today.There is a very cool feature of libgdx called scene2d that basically provides useful

    abstractions for rendering 2D components. Let's understand it better!As of libgdx 0.9.6 some of the classes used on this post were modified or removed. Youmay want to read thepost #13after reading this post, which covers those changes.

    About scene2d

    Scene2d is a module of libgdx that eases the management and rendering of 2Dcomponents, which are called Actors. These actors live on a tree structure inside acontainer called Stage. They keep track of many rendering attributes, such as positionrelative to the parent, color, visibility, dimensions, scale and rotation factors and more. They

    are also responsible for hit detection.

    Examples of actors would be: buttons, text fields, images, enemy targets, coins, the shipwe'll fly, shots and so on. We'll use scene2d a lot in our game. Also, it's possible to applyactions on the actors, like translate, rotate, scale and fade actions. If needed, you can alsowrite your own action, but we'll get to that later.

    I'll try and summarize the main concepts of scene2d below: Actor- A 2D component that knows how to draw itself. Group- An actor that contains other actors. Stage - An engine that requests the actors to be drawn and handles userinteraction by distributing touch events to the actors.

    Action- A function that modifies the actors' properties over time.

    The following UML diagram shows it graphically:

    Using scene2d

    The first thing to do is setup a Stagewhere the actors will act. A nice place for it to live iswithin a screen (each screen with its own stage). These are the steps for managing the

    stage in our game:1. Create a Stage instance on the constructor of AbstractScreen class2. Resize its viewport when the screen is resized3. Call the Stage's act and draw methods within the screen's render method4. Dispose the stage within the screen's dispose method

    For further details, please have a look at the source code of the AbstractScreen class.Now, everything we have to do is add actors to the stage. Our actors should overridemethods like act() and draw(), which are automatically invoked by the stage on theappropriate time.

    Modifying the Splash screen

    We want the splash image to be an actor so that we can do cool things with it. Instead ofextending the Actor class, we can just use the Image

    http://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/AbstractScreen.java?spec=svn8&r=8https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/AbstractScreen.java?spec=svn8&r=8http://yuml.me/diagram/scruffy;/class/%5bActor%5d++-0..*%3E%5bAction%5d,%20%5bActor%5d%5E-%5bGroup%5d,%20%5bGroup%5d++-0..*%3E%5bActor%5d,%20%5bStage%5d-root%3E%5bGroup%5dhttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/AbstractScreen.java?spec=svn8&r=8http://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.html
  • 8/10/2019 Tutorial libgdx

    13/47

    (com.badlogic.gdx.scenes.scene2d.ui.Image) actor that comes with scene2d. We still haveto load the texture and the texture region. We should also tell the image how to be drawn,and that it should take all the screen size. Have a look at thesource code for the modifiedSplashScreen classto see how it's done.

    Regarding that "actions" object inside SplashScreen.resize(), that's the coolest thing we

    can do with scene2d. Each actor can be animated by actions. What we do with our splashimage is to add a set of the following actions:

    1. Fade-in in 0.75 seconds2. Wait for 1.75 seconds3. Fade-out in 0.75 seconds

    We want also to move on to the next screen when the action is completed, so all we haveto do is add a completion listener to the "actions" object. Note that we didn't need tooverride the screen's render method on the splash screen, because the splash image is anactor that was added to the stage, and the stage is the one in charge of rendering it.

    More about actions

    The three actions we used on the splash image ship with libgdx. That is: Fade-in action:com.badlogic.gdx.scenes.scene2d.actions.FadeIn Fade-out action:com.badlogic.gdx.scenes.scene2d.actions.FadeOut Delay action:com.badlogic.gdx.scenes.scene2d.actions.Delay

    I suggest you take some time to check all the available actions inside thepackagecom.badlogic.gdx.scenes.scene2d.actions, but basically there are two types ofthem: Concrete actions - Actions that modifies the actors directly (FadeIn, FadeOut,MoveBy, RotateBy, ScaleTo, etc). Support actions - Actions that group or organize other actions in specific ways

    (Delay, Sequence, Parallel, Repeat, Forever, etc).Every action comes with a factory method named "$" that takes the required parameters forit to work properly. If you want to create a FadeIn action that lasts 2 seconds, you can justwrite:?1 FadeIn fadeInAction = FadeIn.$(2f);

    The delay action has a different factory method. The following code creates the FadeOutaction 5 seconds delayed.?1

    2

    FadeOut fadeOutAction = FadeOut.$(2f);Delay delayAction = Delay.$(fadeOutAction, 5f);

    In order to have the complete effect we want, we need to join both actions:

    ?1 Sequence sAction = Sequence.$(fadeInAction, delayAction);

    And finally, add the action to the actor:?1 actor.action(sAction);

    Piece of cake, isn't it? The last thing about actions I should not forget, is that you can alsoadd interpolators to them. That means your action can start slow and them accelerategradually, for instance. Interpolators define the rate of change for a given animation. Inlibgdx they also come with the "$" factory method, so you can easily create them.

    Domain model

    https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/SplashScreen.java?r=8https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/SplashScreen.java?r=8https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/SplashScreen.java?r=8https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/SplashScreen.java?r=8http://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/FadeIn.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/FadeIn.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/FadeIn.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/FadeOut.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/FadeOut.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/FadeOut.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/Delay.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/Delay.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/Delay.javahttp://code.google.com/p/libgdx/source/browse/#svn%2Ftrunk%2Fgdx%2Fsrc%2Fcom%2Fbadlogic%2Fgdx%2Fscenes%2Fscene2d%2Factionshttp://code.google.com/p/libgdx/source/browse/#svn%2Ftrunk%2Fgdx%2Fsrc%2Fcom%2Fbadlogic%2Fgdx%2Fscenes%2Fscene2d%2Factionshttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://steigert.blogspot.com.br/2012/02/3-libgdx-tutorial-scene2d.htmlhttp://code.google.com/p/libgdx/source/browse/#svn%2Ftrunk%2Fgdx%2Fsrc%2Fcom%2Fbadlogic%2Fgdx%2Fscenes%2Fscene2d%2Factionshttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/Delay.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/FadeOut.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/scenes/scene2d/actions/FadeIn.javahttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/SplashScreen.java?r=8https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/SplashScreen.java?r=8
  • 8/10/2019 Tutorial libgdx

    14/47

    A domain model describes the entities, their attributes, roles and relationships for a givendomain of interest. Most of our business logic will stay on the domain entities, because insoftware engineering it's a nice idea to isolate the business logic. It makes the codestraightforward to other programmers, and even to ourselves later on. It also helps whenswitching technologies. Say we want to switch from 2D to 3D graphics. If the business logicis isolated, the impact of this change will be kept to a minimum.

    With the help of scene2d we can achieve this objective rather easy, as each of thedrawable domain entities can map to a scene2d actor. That's how we'll separate thebusiness code from the presentation code. So the next task for us is to define our domainmodel. In order to make it simple, we're not going to implement a full clone of Tyrian, butjust the main aspects of it. After analyzing the game for some time, I came up with thefollowing diagram:

    You can check the complete source code for the domain modelhere.

    Conclusion

    This post was an introduction to scene2d. We saw how easy it is to use it, and how toextend it with custom functionality. We added a fade effect to the splash image and now itlooks like a real splash image. Finally, we implemented the domain model for our game inan isolatted manner. The tag for this post on the Subversion ishere.Thanks for reading!

    TableLayoutLibgdx version used on this post: 0.9.2 (download)

    It's time to start coding our great menu screen. Before digging into it, let's have a look at

    some important changes I made to the source code.As of libgdx 0.9.6 some of the classes used on this post were modified or removed. Youmay want to read thepost #13after reading this post, which covers those changes.

    Important changes Updated the version of libgdx to the latest version available. All I had to do wasdownload the latest build and replace the JARs and .so files on both projects ( tyrian-androidand tyrian-game). We should get used to this procedure. Changes on AbstractScreen: The collaborators (BitmapFont, SpriteBatch, Skin) are now lazily loaded. The show() method redirects the input processing to our Stage objectthrough: Gdx.input.setInputProcessor( stage ); The hide() method calls dispose(), or the dispose() method would never be

    called at all.

    TWL - Themable Widget Library

    Libgdx includes a library called TWL. This is the description copied from theirofficial web-site:TWL is a graphical user interface library for Java built on top of OpenGL. It provides arich set of standard widgets including labels, edit fields, tables, popups, tooltips, frames anda lot more. Different layout container are available to create even the most advanced userinterfaces.

    When compared to libgdx's scene2d's UI abstractions, TWL is way more powerful. Itcontains even an HTML renderer capable of processing CSS configuration. You can fully

    https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fdomainhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fdomainhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fdomainhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120227https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120227https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120227http://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://libgdx.badlogicgames.com/nightlies/libgdx-nightly-latest.ziphttp://libgdx.badlogicgames.com/nightlies/libgdx-nightly-latest.ziphttp://twl.l33tlabs.org/http://twl.l33tlabs.org/http://twl.l33tlabs.org/http://twl.l33tlabs.org/http://twl.l33tlabs.org/http://twl.l33tlabs.org/http://libgdx.badlogicgames.com/nightlies/libgdx-nightly-latest.ziphttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://code.google.com/p/libgdx/downloads/listhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120227https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fdomain
  • 8/10/2019 Tutorial libgdx

    15/47

    customize your widgets using a tool called TWL Theme Editor (Java Web-Start link). Forthe sake of simplicity we'll stick to scene2d, but in the future we can move up to TWL.

    Some examples of TWL GUIs:

    Creating the Menu Screen with scene2d

    Using the TextButton (com.badlogic.gdx.scenes.scene2d.ui.TextButton) actor that comeswith libgdx we could create a simple menu screen with three buttons: "Start game","Options" and "Hall of Fame". Let's remember the screen flow published on a previouspost:

    We'll use the following screen layout: A welcome message should be displayed at the top. The three buttons should appear sequentially. All components should be vertically aligned on the center of the screen.I know, a designer would be welcome here. But let's focus on the code. :)The most straightforward way of implementing this would be to assign absolute values tothe (x,y) coordinates of each actor. If we did that, we'd come up with the following code:?1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    package

    com.blogspot.steigert.tyrian.screens;

    importcom.badlogic.gdx.scenes.scene2d.Actor; importcom.badlogic.gdx.scenes.scene2d.ui.ClickListener; importcom.badlogic.gdx.scenes.scene2d.ui.Label; importcom.badlogic.gdx.scenes.scene2d.ui.TextButton; importcom.blogspot.steigert.tyrian.Tyrian;

    publicclassMenuScreenextends

    AbstractScreen {

    // setup the dimensions of the menu buttonsprivatestaticfinalfloatBUTTON_WIDTH = 300f;privatestaticfinalfloatBUTTON_HEIGHT = 60f;privatestaticfinalfloatBUTTON_SPACING = 10f;

    publicMenuScreen(Tyrian game )

    {

    http://twl.l33tlabs.org/themer/themer.jnlphttp://twl.l33tlabs.org/themer/themer.jnlphttp://twl.l33tlabs.org/themer/themer.jnlphttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://4.bp.blogspot.com/-gaPHPYAYLXE/Tz0sW7nedlI/AAAAAAAAA8s/-pkqeBadzEc/s1600/libgdxTutorialGameScreens.pnghttp://1.bp.blogspot.com/-7kLQcq-bCBQ/T04bIY3rcII/AAAAAAAAA94/ONiwWO_MSkc/s1600/screen_blue.pnghttp://1.bp.blogspot.com/-IW3nWuiX3f4/T04bCaCPzBI/AAAAAAAAA9s/nSM1YiQCCbw/s1600/screen_simple.pnghttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://twl.l33tlabs.org/themer/themer.jnlp
  • 8/10/2019 Tutorial libgdx

    16/47

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    super( game );}

    @Overridepublicvoidresize(

    intwidth,intheight )

    {

    super.resize( width, height );finalfloatbuttonX = ( width - BUTTON_WIDTH ) / 2; floatcurrentY = 280f;

    // label "welcome"Label welcomeLabel = newLabel( "Welcome to Tyrian for Android!", getSkin() ); welcomeLabel.x = ( ( width - welcomeLabel.width ) / 2);welcomeLabel.y = ( currentY + 100);stage.addActor( welcomeLabel );

    // button "start game"TextButton startGameButton = newTextButton( "Start game", getSkin() );startGameButton.x = buttonX;startGameButton.y = currentY;startGameButton.width = BUTTON_WIDTH;startGameButton.height = BUTTON_HEIGHT;

    stage.addActor( startGameButton );

    // button "options"TextButton optionsButton = newTextButton( "Options", getSkin() );optionsButton.x = buttonX;optionsButton.y = ( currentY -= BUTTON_HEIGHT + BUTTON_SPACING );optionsButton.width = BUTTON_WIDTH;optionsButton.height = BUTTON_HEIGHT;stage.addActor( optionsButton );

    // button "hall of fame"TextButton hallOfFameButton = newTextButton( "Hall of Fame", getSkin() );hallOfFameButton.x = buttonX;hallOfFameButton.y = ( currentY -= BUTTON_HEIGHT + BUTTON_SPACING );hallOfFameButton.width = BUTTON_WIDTH;hallOfFameButton.height = BUTTON_HEIGHT;

    stage.addActor( hallOfFameButton );

    }}

    And the following result:

    All right, it does the job. But is this code easy to read and maintain? It contains many

    mathematical calculations just to come up with screen coordinates for a simple design.Let's try something better.

    http://1.bp.blogspot.com/-QEt090t0ITY/T0_7TjLp7VI/AAAAAAAAA-E/mALjXxcWD84/s1600/Screen%2BShot%2B2012-03-01%2Bat%2B7.41.50%2BPM.png
  • 8/10/2019 Tutorial libgdx

    17/47

    Introducing the TabletLayout

    We could use the TableLayout project, which: is a lightweight library for describing a

    hierarchy of GUI widgets and laying them out using rows and columns. Assembling GUIwidgets in code is clumsy and obfuscates the widget hierarchy. TableLayout provides itsown simple and concise language for expressing the GUI. TableLayout cleanly describesan entire object graph of GUI widgets, leaving the Java code clean and uncluttered.

    That's exactly what we want, and these are the steps to follow:1. Open the TableLayout Editorapplication (Java Web-Start link).2. Write the layout we want following theeditor's language.3. Save the layout descriptor to a text file inside our resources folder.4. On the MenuScreen class, load this layout descriptor and make finalconfigurations.

    With the following layout descriptor:?1

    2

    3

    4

    5

    6

    7

    8

    9

    debug* spacing:10 padding:0 align:center'Welcome to Tyrian for Android!' spacingBottom:20---[startGameButton] width:300 height:60---[optionsButton] width:300 height:60---[hallOfFameButton] width:300 height:60

    I got the following preview:

    Now we should save this layout descriptor inside our resources folder. I saved it under:tyrian-android/assets/layout-descriptors/menu-screen.txtThe final step is to modify our MenuScreenclass to read this file, as follows:?1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    packagecom.blogspot.steigert.tyrian.screens;

    importcom.badlogic.gdx.Gdx; importcom.badlogic.gdx.scenes.scene2d.Actor; importcom.badlogic.gdx.scenes.scene2d.ui.ClickListener; importcom.badlogic.gdx.scenes.scene2d.ui.Label; importcom.badlogic.gdx.scenes.scene2d.ui.Skin; importcom.badlogic.gdx.scenes.scene2d.ui.TextButton; importcom.badlogic.gdx.scenes.scene2d.ui.tablelayout.Table;

    importcom.badlogic.gdx.scenes.scene2d.ui.tablelayout.TableLayout; importcom.blogspot.steigert.tyrian.Tyrian;

    http://code.google.com/p/table-layout/http://code.google.com/p/table-layout/http://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://code.google.com/p/table-layout/#Languagehttp://code.google.com/p/table-layout/#Languagehttp://code.google.com/p/table-layout/#Languagehttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://3.bp.blogspot.com/-w1LBdzEPQnQ/T1AFmJunopI/AAAAAAAAA-Y/wO1pxpAbg84/s1600/Screen+Shot+2012-03-01+at+8.25.56+PM.pnghttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://steigert.blogspot.com.br/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://code.google.com/p/table-layout/#Languagehttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://code.google.com/p/table-layout/
  • 8/10/2019 Tutorial libgdx

    18/47

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    6566

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    publicclassMenuScreenextends

    AbstractScreen {

    publicMenuScreen(Tyrian game )

    {super( game );

    }

    @Overridepublicvoidresize(

    intwidth,intheight )

    {super.resize( width, height );

    // retrieve the skin (created on the AbstractScreen class) Skin skin = super.getSkin();

    // create the table actorTable table = newTable( getSkin() );table.width = width;table.height = height;

    // add the table to the stage and retrieve its layout stage.addActor( table );TableLayout layout = table.getTableLayout();

    // [edit] this section is not needed// register the label "welcome"// Label welcomeLabel = new Label( "Welcome to Tyrian for Android!", skin ); // layout.register( "welcomeMessage", welcomeLabel );

    // register the button "start game"TextButton startGameButton = newTextButton( "Start game", skin );startGameButton.setClickListener( newClickListener() {

    @Overridepublicvoidclick(

    Actor actor,

    float

    x,

    floaty ){

    game.setScreen( game.getStartGameScreen() );}

    } );layout.register( "startGameButton", startGameButton );

    // register the button "options"TextButton optionsButton = newTextButton( "Options", skin );optionsButton.setClickListener( newClickListener() {

    @Overridepublicvoidclick(

    Actor actor,floatx,floaty )

    {

    game.setScreen( game.getOptionsScreen() );}

    } );layout.register( "optionsButton", optionsButton );

    // register the button "hall of fame"TextButton hallOfFameButton = newTextButton( "Hall of Fame", skin );hallOfFameButton.setClickListener( newClickListener() {

    @Overridepublicvoidclick(

    Actor actor,floatx,floaty )

    {game.setScreen( game.getHallOfFameScreen() );

    }

    } );layout.register( "hallOfFameButton", hallOfFameButton );

  • 8/10/2019 Tutorial libgdx

    19/47

    // finally, parse the layout descriptorlayout.parse( Gdx.files.internal( "layout-descriptors/menu-screen.txt").readString() );

    }}

    And that's it! All mathematical calculations are gone. I also set the listeners to the buttons,so that they move the player to other screens. Note that I'm just adding the table to the

    stage. The other actors are registered on the table's layout, using the same IDs containedin the layout descriptor.

    Conclusion

    In this post we saw how to implement a screen layout separating the presentation (layoutdescriptor) from the controller (MenuScreen) code using TableLayout. Now we can easilyread it and modify it when necessary. It took us some effort to learn a new component, butthis effort will certainly pay off as we create and maintain the other screens. I didn't detailthe Skinobject we used because it's not so important at this moment, and I would need anentire post for it. This is thetag on Subversionfor this post. Thanks!

    FilesLibgdx version used on this post: 0.9.2 (download)

    Moving on with our game development, I decided to make a slight change of plans. Insteadof having a "Hall Of Fame" screen I think it would be easier to implement a "High Scores"screen. Let's go through all the steps...

    Important changes

    Resources reorganization: by the time the game is finished we'll have manyresource files, so it's a good idea to organize them now. I created folders for each type ofresource we currently have. That is: skin, image-atlasesand layout-descriptors. Splash screen refactoring:A reader of this tutorial shared a tip on how to betteruse the screen's show/resize methods. In case the screen is resized, our actors don't needto be recreated, but just resized/repositioned. Here is a link containing the changes madeto theSplashScreenandAbstractScreen.Thanks xryz!

    About the High Scores screen

    Following the "design standards" of the Menu screen, the High Scores screen will display

    the best scores on each of the three levels of the game and a button to go back to the mainmenu. Using theTableLayout Editor,I ended up with this layout descriptor:?

    1

    2

    3

    4

    5

    6

    7

    8

    910

    debug* padding:8|| fill:x---'High Scores' colspan:2 spacingBottom:20---'Episode 1'[episode1HighScore]---

    'Episode 2'[episode2HighScore]

    https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120301https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120301https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120301http://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttps://code.google.com/p/steigert-libgdx/source/diff?spec=svn18&old=17&r=18&format=side&path=%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fscreens%2FSplashScreen.javahttps://code.google.com/p/steigert-libgdx/source/diff?spec=svn18&old=17&r=18&format=side&path=%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fscreens%2FSplashScreen.javahttps://code.google.com/p/steigert-libgdx/source/diff?spec=svn18&r=18&format=side&path=/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/AbstractScreen.javahttps://code.google.com/p/steigert-libgdx/source/diff?spec=svn18&r=18&format=side&path=/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/AbstractScreen.javahttps://code.google.com/p/steigert-libgdx/source/diff?spec=svn18&r=18&format=side&path=/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/AbstractScreen.javahttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttps://code.google.com/p/steigert-libgdx/source/diff?spec=svn18&r=18&format=side&path=/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/AbstractScreen.javahttps://code.google.com/p/steigert-libgdx/source/diff?spec=svn18&old=17&r=18&format=side&path=%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fscreens%2FSplashScreen.javahttp://code.google.com/p/libgdx/downloads/listhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120301
  • 8/10/2019 Tutorial libgdx

    20/47

    11

    12

    13

    14

    15

    1617

    ---'Episode 3'[episode3HighScore]---[backButton] colspan:2 width:300 height:60 spacingTop:30

    And this preview:

    Note:We talked about the TableLayout in aprevious post.

    The code for theHighScores screenis pretty similar to theMenu screen,so I won't go intodetails.

    The Profile domain entity

    We should now think where these high scores will come from. Looking at our domainmodel, the Profile class seems to be a good candidate since it contains other attributes thatshould also be persisted across game restarts, such as the "credits" available to the playerand the current level she/he is currently at. This is the only domain entity we'll persist to adata store.

    We can use a simple Mapattribute for holding the current best scores, using level IDs asthe keys. Please have a look at the Profile class.The Level class was also changed toinclude an ID attribute.

    Handling preferences in libgdx

    One data store available to us is thepreferences mechanism. It can hold simple key-valuepairs and uses the SharedPreferencesclass on Android, and a flat file on Desktop. Have alook at the source code for the libgdx'sPreferences interfaceand check out the methods itdeclares. This is an usage example:?

    1

    2

    Preferences prefs = Gdx.app.getPreferences( "profile");

    prefs.putInteger( "credits", 1000);We could do it using preferences. It's simple and effective. But let's think a little bit more.

    http://steigert.blogspot.com/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://steigert.blogspot.com/2012/03/4-libgdx-tutorial-tablelayout.htmlhttp://steigert.blogspot.com/2012/03/4-libgdx-tutorial-tablelayout.htmlhttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/HighScoresScreen.java?spec=svn18&r=18https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/HighScoresScreen.java?spec=svn18&r=18https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/HighScoresScreen.java?spec=svn18&r=18https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/MenuScreen.java?spec=svn18&r=18https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/MenuScreen.java?spec=svn18&r=18https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/MenuScreen.java?spec=svn18&r=18https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/domain/Profile.java?spec=svn18&r=19https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/domain/Profile.java?spec=svn18&r=19https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/domain/Level.java?spec=svn17&r=17https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/domain/Level.java?spec=svn17&r=17http://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/Preferences.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/Preferences.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/Preferences.javahttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://4.bp.blogspot.com/-noUNpSsqxIA/T1V43WRP8hI/AAAAAAAAA-k/RnQHgBjwBJU/s1600/HighScoresScreen.pnghttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/Preferences.javahttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/domain/Level.java?spec=svn17&r=17https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/domain/Profile.java?spec=svn18&r=19https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/MenuScreen.java?spec=svn18&r=18https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/HighScoresScreen.java?spec=svn18&r=18http://steigert.blogspot.com/2012/03/4-libgdx-tutorial-tablelayout.html
  • 8/10/2019 Tutorial libgdx

    21/47

    Some day we might release an updated version of our game. The Profile class mayhave changed, so we'd have to read the old preferences and updated them to the newpreferences format. There is a chance of the user to try and manually edit this preferences file in orderto gain advantages in the game. As we're just persisting the state of one domain entity, maybe it's easier to have its

    whole state serialized and deserialized later on. We could add support for multiple profiles in the future.Given that, let's see a second data store option.Edit:in thepost #6I wrote about the preferences mechanism.

    Handling files in libgdx

    The files module is another abstraction of libgdx. It lets you handle files the same waywhether you're running the game on Android or on the Desktop. In the source coderepository of libgdx we can also see some new backends, like GWT and iOS. Soabstractions are a nice thing to have.

    These are the available file types in libgdx:1. Classpath: Read-only files that live in the root of the compiled classes.2. Internal: Read-only files found in the root of the application (in the case of aDesktop), or inside the assets folder (on Android).3. External: Read/Write files located in the user's home directory (in the caseof a Desktop), or in the root of the SD Card (on Android).4. Absolute: Files referred to through absolute paths, which is normally not acool thing.5. Local: Read/Write files located in the game's root directory (in the case of aDesktop), or in the private application folder (on Android). This file typewas addedrecentlyand is very interesting. The advantages of using it on Android is that: (1)these files are private to the application, (2) in case the application is removed, so

    are these files, (3) there is no availability problem (the SD Card is not alwaysavailable).

    Like in Java, a directory is considered to be a File in libgdx, but instead of re-using thejava.io.File class, the com.badlogic.gdx.files.FileHandleclass is used (independent ofthe file's type). The following lines show how to create a FileHandle for each of theavailable file types:

    1. Gdx.files.classpath( "config.properties" );2. Gdx.files.internal( "data/config.properties" );3. Gdx.files.external( ".tyrian/game-progress.xml" );4. Gdx.files.absolute("/Users/gustavo.steigert/.tyrian/game-progress.xml" );5. Gdx.files.local( "data/game-progress.xml" );

    Now that we know how to create file handles, the following list shows what we can do withthem: Read: there are many read methods, but the simplest one is readString(), whichreturns a String with the contents of the file. Write: there are also many write methods. The simplest is writeString(content,append/overwrite). List: in case it points to a directory, calling list() retrieves an array of FileHandlewith the children. Delete:erases the file with delete(), or the directory with deleteDirectory(). Copy/Move: copies/moves the file/directory withcopyTo(destination)/moveTo(destination).In case some of these operations fail, a GdxRuntimeException will be thrown (which is a

    RuntimeException).Edit:this section was updated to cover the new file type "internal". In a next post we'll usethis new file type instead of "external".

    http://steigert.blogspot.com/2012/03/6-libgdx-tutorial-preferences.htmlhttp://steigert.blogspot.com/2012/03/6-libgdx-tutorial-preferences.htmlhttp://steigert.blogspot.com/2012/03/6-libgdx-tutorial-preferences.htmlhttp://www.badlogicgames.com/wordpress/?p=2305http://www.badlogicgames.com/wordpress/?p=2305http://www.badlogicgames.com/wordpress/?p=2305http://www.badlogicgames.com/wordpress/?p=2305http://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/files/FileHandle.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/files/FileHandle.javahttp://code.google.com/p/libgdx/source/browse/trunk/gdx/src/com/badlogic/gdx/files/FileHandle.javahttp://www.badlogicgames.com/wordpress/?p=2305http://www.badlogicgames.com/wordpress/?p=2305http://steigert.blogspot.com/2012/03/6-libgdx-tutorial-preferences.html
  • 8/10/2019 Tutorial libgdx

    22/47

    Using the JSON format

    When writing to a text file we'll use theJSON notationto model our data. If you don't know

    JSON yet you should really take the time to read about it. JSON is gradually replacing XMLsince its output is easier for humans and machines to read, and the output is not solengthy.

    We'll make the Profile class know how to generate a JSON representation of its state, andalso how to recover it later on. The first thing to do is make the Profile class implementcom.badlogic.gdx.utils.Json.Serializable, which declares the following methods:?

    1

    2

    3

    4

    public interface Serializable {void write (Json json);void read (Json json, OrderedMap jsonData);

    }

    The Javadocs for the JSON classes in libgdx are missing, so reading the source code andanalysing other projects I came up with the following implementation in our Profile class:?1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    1213

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    @SuppressWarnings( "unchecked" )@Overridepublic void read(

    Json json,OrderedMap jsonData )

    {currentLevelId = json.readValue( "currentLevelId", Integer.class, jsonData ); credits = json.readValue( "credits", Integer.class, jsonData );

    // libgdx handles the keys of JSON formatted HashMaps as Strings, but we // want it to be integers instead (because the levelIds are integers)

    Map highScores = json.readValue( "highScores", HashMap.class,Integer.class, jsonData );

    for( String levelIdAsString : highScores.keySet() ) {int levelId = Integer.valueOf( levelIdAsString );Integer highScore = highScores.get( levelIdAsString);this.highScores.put( levelId, highScore );

    }}

    @Overridepublic void write(

    Json json ){

    json.writeValue( "currentLevelId", currentLevelId );json.writeValue( "credits", credits );json.writeValue( "highScores", highScores );

    }

    The method calls are pretty intuitive. We're writing key-values in write() and recoveringthem with the correct types in read(). These methods still lack the code for saving/restoringthe Ship's items. I think you can easily handle it, so I won't go into details.

    The Profile service

    The last piece of the puzzle is to create a service to coordinate the reading and writing ofthe Profile's state. It should define the location of the target file and expose read/write

    operations, also handling eventual unexpected states. These are the detailed requirementsfor the Profile service:1. Retrieve Profile operation

    http://www.json.org/http://www.json.org/http://www.json.org/http://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://www.json.org/
  • 8/10/2019 Tutorial libgdx

    23/47

    The target file that will hold the state of the Profile class will be an externalfile located at: .tyrian/profile-v1.json; The "-v1" suffix allows us to update the filemodel when new versions of the game get installed. If the target file does not exist, one should be created based on an emptyProfile state. If the target file exists, it should be read and supplied to a fresh Profile

    instance, so that it can restore its state. The content of the file will be encoded with thecom.badlogic.gdx.utils.Base64Coderutility of libgdx, making it harder for playerstrying to edit it manually. Should any problem occur while reading the file or restoring the Profilestate, a fresh new Profile should be created and retrieved to the caller.2. Persist Profile operation Have the given Profile instance generate its own JSON representation. Encode the outcome with libgdx's com.badlogic.gdx.utils.Base64Coder. Write the result to the target file.

    If the player opens the target file, something like this will be displayed:?1

    e2N1cnJlbnRMZXZlbElkOjAsY3JlZGl0czowLGhpZ2hTY29yZXM6eyIwIjoxMDAwLCIxIjoyNDAwLCIyIjo1MjAwfX0=

    Editing it will likely invalidate the structure, making the decode operation impossible. In thiscase, we just create a new Profile and move on with life.

    You can find the source code for the Profile class here. Going back to the HighScorescreen, it should now retrieve the Profile through the Profile service, and simply display thehigh scores for each level. This is the resulting screen for all this hard work (amazing, isn'tit?):

    ConclusionWhen dealing with the High Scores screen (old Hall Of Fame screen) we learned how touse files in libgdx. We went a bit further and talked about the other persistence mechanism(preferences), did some basic encoding to the game progress data and played with someJSON utility classes. This is thetag on the Subversionfor this post. Thanks for reading!

    PreferencesLibgdx version used on this post: 0.9.2 (download)

    Our next target is the Options screen.

    About the Options screen

    http://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.htmlhttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/services/ProfileService.java?spec=svn19&r=19https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/services/ProfileService.java?spec=svn19&r=19https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120306https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120306https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120306http://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://3.bp.blogspot.com/-rops8aGGS70/T1bHzEUeuqI/AAAAAAAAA-w/x3zj4p3SNC8/s1600/HighScoreScreen.pnghttp://code.google.com/p/libgdx/downloads/listhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120306https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/services/ProfileService.java?spec=svn19&r=19http://steigert.blogspot.com.br/2012/03/5-libgdx-tutorial-files.html
  • 8/10/2019 Tutorial libgdx

    24/47

    Once again we'll implement a very simple screen with the following options: Sound Effects (enabled/disabled) Music (enabled/disabled) Volume (0-100)Using theTableLayout EditorI wrote the following layout descriptor:?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    debug* padding:8---'Options' colspan:3 spacingBottom:20---'Sound Effects'[soundEffectsCheckbox] colspan:2 align:left---'Music'[musicCheckbox] colspan:2 align:left---

    'Volume'

    [volumeSlider] fill:x[volumeValue] width:40---[backButton] colspan:3 width:300 height:60 spacingTop:30

    Now we should think where to save these values.

    Preferences mechanism

    It makes no sense to store these option values in our domain model. The Profile classdeals specifically with domain attributes that must be saved. Using files is a valid option,but we can use something simpler: the preferences mechanism. We can add just key-valuepairs to it and we don't need to care on how and where this information is going to bepersisted. Seems enough.

    The first thing we should do is implement a class that abstracts the access to thepreferences mechanism. The following class does the job:?1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    2122

    23

    packagecom.blogspot.steigert.tyrian;

    importcom.badlogic.gdx.Gdx;importcom.badlogic.gdx.Preferences;

    /*** Handles the game preferences.*/

    publicclassTyrianPreferences{

    // constantsprivatestaticfinalString PREF_VOLUME = "volume";privatestaticfinalString PREF_MUSIC_ENABLED = "music.enabled";privatestaticfinalString PREF_SOUND_ENABLED = "sound.enabled";privatestaticfinalString PREFS_NAME = "tyrian";

    publicTyrianPreferences(){}

    protectedPreferences getPrefs(){returnGdx.app.getPreferences( PREFS_NAME );

    http://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlphttp://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.htmlhttp://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.htmlhttp://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.htmlhttp://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.htmlhttp://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.htmlhttp://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.htmlhttp://table-layout.googlecode.com/svn/wiki/jws/editor.jnlp
  • 8/10/2019 Tutorial libgdx

    25/47

    24

    25

    26

    27

    28

    29

    30

    3132

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    }

    publicbooleanisSoundEffectsEnabled(){

    returngetPrefs().getBoolean( PREF_SOUND_ENABLED, true);}

    publicvoidsetSoundEffectsEnabled(booleansoundEffectsEnabled )

    {getPrefs().putBoolean( PREF_SOUND_ENABLED, soundEffectsEnabled );getPrefs().flush();

    }

    publicbooleanisMusicEnabled(){

    returngetPrefs().getBoolean( PREF_MUSIC_ENABLED, true);}

    publicvoidsetMusicEnabled(booleanmusicEnabled )

    {getPrefs().putBoolean( PREF_MUSIC_ENABLED, musicEnabled );getPrefs().flush();

    }

    publicfloatgetVolume(){

    returngetPrefs().getFloat( PREF_VOLUME, 0.5f );}

    publicvoidsetVolume(floatvolume )

    {getPrefs().putFloat( PREF_VOLUME, volume );getPrefs().flush();

    }}

    These are the codes I'd like you to notice:1. Gdx.app.getPreferences( PREFS_NAME );2. getPrefs().flush();

    The first one creates the preferences object if needed, then retrieves it to the caller. In theDesktop, this file would be saved under %USER_HOME%/.prefs/tyrian , but just when thesecond command gets executed. We should always flush the preferences after modifyingthem. On Android devices, theSharedPreferencesclass would be used, so all preferencesare managed by the built-in preferences mechanism.

    Implementing the Options screen

    Before implementing the Options screen we could modify our Tyrian class to hold aninstance of TyrianPreferences. Now we can finally code our screen. I won't paste thesource code here because it is a bit lengthy, so I ask you to see it here.This is what we'redoing: Like the other screens we create the scene2d actors and register them in ourTableLayout. Now we're handling two new kinds of actors: CheckBox and Slider. We register listeners on these actors so that we do something when their state getschange. Basically we call our TyrianPreferences class, which abstracts the usage of thepreferences mechanism.This is the resulting screen:

    http://developer.android.com/reference/android/content/SharedPreferences.htmlhttp://developer.android.com/reference/android/content/SharedPreferences.htmlhttp://developer.android.com/reference/android/content/SharedPreferences.htmlhttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/OptionsScreen.java?spec=svn21&r=21https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/OptionsScreen.java?spec=svn21&r=21https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/OptionsScreen.java?spec=svn21&r=21https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/OptionsScreen.java?spec=svn21&r=21http://developer.android.com/reference/android/content/SharedPreferences.html
  • 8/10/2019 Tutorial libgdx

    26/47

    We don't need to know how the preferences are saved on the Desktop, but in case you're

    curious about it, this is the preferences file created by libgdx:?

    1

    2

    3

    4

    5

    6

    7

    true 0.5 true

    Conclusion

    We created the Options screen using new actors (checkbox and slider) and made themsave preference values through an abstraction to the preferences mechanism. Note that wedon't manage any audio yet, but we'll do it soon. This is thetag for this poston Subversion.Thanks for reading!

    Texture PackerLibgdx version used on this post: 0.9.2 (download)

    In this post we will implement the last screen of the menu, the Start Gamescreen.As of libgdx 0.9.6 some of the classes used on this post were modified or removed. Youmay want to read thepost #13after reading this post, which covers those changes.

    Important changes Inside the pause method of our game (Tyrian#onPause) I'm now callingProfileService#persist. This causes the game progress to be saved to an external file as Iexplained in aprevious post.It's important to put this method call inside the pause methodbecause we don't know if the player is coming back to the game after this method isexecuted. I also added a flag to define the current runtime environment (development orproduction). This way we can change the game's default functionality to ease itsdevelopment. One example of that is during the persistence of the game progress(ProfileService#persist). If we're in development mode there is no need to encode theoutput file, so we can easily open and edit it with a text editor. I also had to modify the codethat reads the game progress to check if its contents are encoded or not.

    http://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.htmlhttp://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.htmlhttp://java.sun.com/dtd/properties.dtdhttp://java.sun.com/dtd/properties.dtdhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120307https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120307https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120307http://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://code.google.com/p/libgdx/downloads/listhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://steigert.blogspot.com/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com/2012/03/5-libgdx-tutorial-files.htmlhttp://4.bp.blogspot.com/-0704s6KBqxk/T1gak1cbNVI/AAAAAAAAA-4/5T0dgHWBYfA/s1600/OptionsScreen.pnghttp://steigert.blogspot.com/2012/03/5-libgdx-tutorial-files.htmlhttp://steigert.blogspot.com.br/2012/07/13-libgdx-tutorial-libgdx-refactoring.htmlhttp://code.google.com/p/libgdx/downloads/listhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftags%2Fpost-20120307http://java.sun.com/dtd/properties.dtdhttp://steigert.blogspot.com.br/2012/03/6-libgdx-tutorial-preferences.html
  • 8/10/2019 Tutorial libgdx

    27/47

    Many changes were made to the domain entities in order to facilitate its usage. Isuggest you browse each domain class using thesource code browser.

    About the Start Game screen

    This screen will have the following roles:1. Enable the selection of the level to be played.2. Customize the ship.3. Display the credits available to the player.

    I'm considering you read the previous posts, so I'll summarize the steps involved: createthelayout descriptorand writethe screen's codethat reads it and configures the widgets.

    On this screen we used a new widget, theSelectBox.Like the other widgets, it allows us toset a listener so that we get notified when the player change the selection. Inside thislistener we coordinate the operation to be executed, delegating most of the checks to thedomain objects.

    OpenGL and Textures

    The Start Game screen works nice, but it is definitely not fun to customize the ship usingonly form widgets. We should at least show an image of the installed items. We've beenusing images for some time (in the Splash screen) but we never got into much detail, solet's understand them better.

    In 1992 Silicon Graphics Inc. released OpenGL, an API that defines many abstractionseasing the work with different graphical hardware. One of these abstractions is texturemapping. It basically states that for an image to be displayed it must come from a texturecurrently bound to the GPU's memory. In order to improve performance, we can put

    multiple images inside a texture and later we tell which region of this texture we want todraw. If the image we want to show is inside another texture (other than the currently boundtexture), a texture switching must occur on the GPU before OpenGL can render it. Weshould try to minimize this texture switching because it can cause us performance issues.

    In libgdx we can reference a texture using the com.badlogic.gdx.graphics.Texture class.Note that it's very important to dispose a texture if you're not going to using it, because theJava object that points to it is just a pointer, and the actual resource may be consuming theGPU's memory. The Garbage Collector doesn't know anything about the GPU, so makesure you're always disposing the textures correctly.

    Image Atlas

    In the previous section we learned that in order to optimize performance we should putmultiple images inside the same texture (instead of creating one texture for each image).To do so we create the so called "image atlas", carefully picking related images for it. If theimages are not related, we'll end up switching textures too many times at runtime, which isexactly what we want to avoid.

    For the Splash Screen we created an image atlas of one image. That's ok because theimage is large and we didn't need any other image for this screen. But when we startdeveloping the game screen, we'll have to handle several small images. We should keeptrack of the dimension and location of each image inside the image atlas in order to create

    ourTextureRegionobjects later on. We could have our designer handle this, but we don'thave one and event if we had, there is a better approach we can use.

    https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fdomainhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fdomainhttps://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fdomainhttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-android/assets/layout-descriptors/start-game-screen.txt?spec=svn23&r=23https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-android/assets/layout-descriptors/start-game-screen.txt?spec=svn23&r=23https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-android/assets/layout-descriptors/start-game-screen.txt?spec=svn23&r=23https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/StartGameScreen.java?spec=svn25&r=25https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/StartGameScreen.java?spec=svn25&r=25https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/StartGameScreen.java?spec=svn25&r=25http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/SelectBox.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/SelectBox.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/SelectBox.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/SelectionListener.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/SelectionListener.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/Texture.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/Texture.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/TextureRegion.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/TextureRegion.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/TextureRegion.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/TextureRegion.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/Texture.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/SelectionListener.htmlhttp://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/scenes/scene2d/ui/SelectBox.htmlhttps://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-game/src/com/blogspot/steigert/tyrian/screens/StartGameScreen.java?spec=svn25&r=25https://code.google.com/p/steigert-libgdx/source/browse/trunk/tyrian-android/assets/layout-descriptors/start-game-screen.txt?spec=svn23&r=23https://code.google.com/p/steigert-libgdx/source/browse/#svn%2Ftrunk%2Ftyrian-game%2Fsrc%2Fcom%2Fblogspot%2Fsteigert%2Ftyrian%2Fdomain
  • 8/10/2019 Tutorial libgdx

    28/47

    Texture Packer

    Libgdx provides a tool called TexturePackerthat basically creates image atlases for us. Allwe have to do is specify where our individual images are, and where to save the outputimage atlases. If needed, we can also set several settings to customize the output. In order

    to use it, I followed the following steps:1. On Eclipse, I added a new JAR to the tyrian-game's classpath: gdx-tools.jar. You can find this JAR inside the libgdx build you downloaded, orpreferably atthis link,if you're using the latest libgdx version.2. I created a directory to hold the individual images at: tyrian-game/etc/images. This will be our input directory. Then, I created sub-directoriesbased on the screen the images will appear. The TexturePacker uses these sub-directories to group the images, that is, images inside different sub-directories willnever be placed on the same image atlas.3. I created the image files, which you can seehere.4. I created theTyrianTexturePackerclass, which calls the TexturePacker theway we want. Basically, I told the TexturePacker to read the images from our inputdirectory and save the output image atlases to our resource folder. Please read the

    comments inside the class for detailed information.5. I ran the TyrianTexturePackerclass and refreshed my resources folder.

    Texture Packer Configuration

    Instead of going through each configuration option, I'll share agreat linkthat details all theconfiguration that can be done. Note that the image files may follow a special nomenclaturefor setting specific configuration that applies only to the image itself. The underscorecharacter is used to separate these special settings, so don't use it when naming yourimage files unless you want to use this feature.

    As an example of that, I updated theSplash screento also use an image atlas generatedby the Texture Packer. I renamed the splash image to: splash-image_l,l.png. The "l,l" parttells that the linear texture filter should be used when loading the image in runtime.

    Using image atlases

    Finally, I'll show how to load the image atlases generated by the TexturePacker tool.1. I modified the AbstractScreen class to add a newTextureAtlasattribute. Icreate an instance of it in a lazy manner, and dispose it in the dispose method.2. In order to create a TextureAtlas instance, I call:?

    1 atlas = newTextureAtlas( Gdx.files.internal( "image-atlases/pages-info") );

    This "pages-info" file was generated by the TexturePacker, and it contains all theinformation of the generated image atlases.3. When I want to load an image, all I have to do is callgetAtlas().findRegion(), passing in the name of the image file I want. This in turngives me an instance ofAtlasRegion, which extends TextureRegion(the class weused previously in the Splash screen to load the splash image). Check out t