Вы находитесь на странице: 1из 47

Introduction

Libgdx version used on this post: 0.9.2 (download)

Developing games is a very difficult task, even if you want something simple. There are many technical concepts to grasp and then you must learn how to implement them correctly and efficiently on your game's target platform. This is the first post of a tutorial that I'll write about libgdx - an open source high-performance Java framework for developing games. 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. Then you can make it run on an Android device just by writing one line of code. You can even distribute your games as an applet or via Webstart if you want so. Some features of libgdx are implemented using native code (like physics and audio processing functions), which optimizes its execution at runtime.

So libgdx seams to be a nice gaming framework, but what does it provide? I'll highlight its main 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 on embedded systems - including consoles, phones, appliances and vehicles. It consists of well-defined subsets of desktop OpenGL, creating a flexible and powerful low-level interface between software and graphics acceleration. OpenGL ES includes profiles for floating-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 offers acceleration, image quality and performance. OpenGL ES 2.X enables full programmable 3D 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 API specification.

Installing libgdx

I'm considering you've already installed Java, Eclipse and the Android SDK, and everything is working perfectly. Now is the time to download libgdx. I'm using the latest version available for now: 0.9.2. Now, instead of going through all the installation steps involved, I want you to watch this video on YouTube. The guy on the video is Mario Zechner, writer of the Beginning Android Games book and the main programmer of libgdx. He'll guide you through 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 like Tyrian. I used to play this game a lot when I was a kid, 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 using libgdx. The game will improve as I write new posts for the tutorial. I'll put all the source code

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

and came up with the following project structure on Eclipse: The tyrian-android project depends on the

The tyrian-android project depends on the tyrian-game project. This dependency is configured in the "Java Build Path > Projects" section of the tyrian-android project's settings. 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 (although it 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-android project 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 be accessible by both our projects. Instead of duplicating the resources, there is a cool trick we can do in Eclipse. Note that our Android project has an "assets" folder. By the time we have 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 settings "

and click the "Link Source

button as follows:

Open the game project's settings " and click the "Link Source button as follows: The game's

The game's main class

of

com.badlogic.gdx.ApplicationListener in libgdx. You should get used to read the Javadocs of libgdx's classes, because many details won't be covered here. Tip: the source code for the libgdx's JARs can be attached to our projects.

At

the

simplest

form,

a

game's

main

class

is

an

instance

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 same behavior when the game is executed on Android, but they're quite similar. The following list shows 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 the paused state. It is also fired once just after the create event.

render - Fired by the game loop from the application every time the rendering happens. The game update should take place before the actual rendering.

pause - Fired just before the application is destroyed. On Android it happens when the home button is pressed or an incoming call is happening. On desktop it's fired just before disposal when exiting the application. This a good time to save the game state on Android 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 the pause event.

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

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

?

1

package com.blogspot.steigert.tyrian;

2

3

import com.badlogic.gdx.ApplicationListener;

4

import com.badlogic.gdx.Gdx;

5

import com.badlogic.gdx.graphics.FPSLogger;

6

import com.badlogic.gdx.graphics.GL20;

7

8

/**

9

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

10

*/

11

public class Tyrian

12

implements

13

ApplicationListener

14

{

15

// constant useful for logging

16

public static final String LOG = Tyrian.class.getSimpleName();

17

18

// a libgdx helper class that logs the current FPS each second

19

private FPSLogger fpsLogger;

20

21

@Override

22

public void create()

23

{

24

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

25

fpsLogger = new FPSLogger();

26

}

27

28

@Override

29

public void resize(

30

int width,

31

int height )

32

{

33

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

34

}

36

@Override

37

public void render()

38

{

39

// the following code clears the screen with the given RGB color (green)

40

Gdx.gl.glClearColor( 0f, 1f, 0f, 1f );

41

Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT );

42

43

// output the current FPS

44

fpsLogger.log();

45

}

46

47

@Override

48

public void pause()

49

{

50

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

51

}

52

53

@Override

54

public void resume()

55

{

56

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

57

}

58

59

@Override

60

public void dispose()

61

{

62

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

63

}

64

}

Notes on the above code:

As stated previously, our game's main class is just an instance ofcom.badlogic.gdx.ApplicationListener.

With the FPSLogger class it gets easy to output the current FPS each second, so we have a feel about our game's performance.

The Gdx class holds singleton instances of many modules: graphics, audio, files and 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 each one. Let's start with the desktop launcher. Please have a look at the following code and read the inline comments.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

package com.blogspot.steigert.tyrian;

import com.badlogic.gdx.ApplicationListener;

import com.badlogic.gdx.backends.lwjgl.LwjglApplication;

/**

* This class simply creates a desktop LWJGL application.

*/ public class TyrianDesktopLauncher

{

public static void main(

{

String[] args )

// create the listener that will receive the application events

ApplicationListener listener = new Tyrian();

// define the window's title

15

16

17

18

19

20

21

22

23

24

25

26

String title = "Tyrian";

// define the window's size int width = 800, height = 480;

// whether to use OpenGL ES 2.0 boolean useOpenGLES2 = false;

// create the game

new LwjglApplication( listener, title, width, height, useOpenGLES2 );

}

}

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

this class, you should be able to see the following result: And for the Android devices,

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

package com.blogsport.steigert.tyrian;

import android.os.Bundle;

import com.badlogic.gdx.backends.android.AndroidApplication; import com.blogspot.steigert.tyrian.Tyrian;

/**

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

*/ public class TyrianAndroidLauncher extends AndroidApplication

{

@Override

public void onCreate(

Bundle savedInstanceState )

{

super.onCreate( savedInstanceState );

// whether to use OpenGL ES 2.0

boolean useOpenGLES2 = false;

// create the game

21

22

23

24

25

26

27

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

initialize( new Tyrian(), useOpenGLES2 );

}

}

get: initialize( new Tyrian(), useOpenGLES2 ); } } Conclusion We created the project structure for our

Conclusion

We created the project structure for our game and tested it both on the desktop and on the Android device. The next posts will focus on libgdx internals and how to use it to implement the features of our cool game. A project was created on Google Code so that you can easily browse the source code. I created a tag on the Subversion repository for this post's project state. Let me know if I can improve/correct something, and I see you soon!

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:

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

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 extending 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:

might come handy, so the final picture looks like this: I'm not proud of that circular

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:

?

1

2

3

4

5

6

7

public class Tyrian

{

extends Game

public SplashScreen getSplashScreen()

{

return new SplashScreen( this );

}

8

9

10

11

12

13

14

15

16

17

18

19

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.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

@Override

public void create()

{

 

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

setScreen( getSplashScreen() );

}

(

)

}

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:

cool image we could use for our splash screen. Check it out: Sadly we cannot just

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

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:

?

1

2

3

4

5

6

7

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

33

34

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 );

}

36

@Override

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

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

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();

}

}

  super.dispose(); splashTexture.dispose(); } } Conclusions We improved our main game project to support

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!

scene2d

Libgdx version used on this post: 0.9.2 (download)

All we have by now is a splash screen that displays an static image. Wouldn't it be nice to add 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 handle things like selecting options, clicking on buttons, giving focus to an input widget and so on so 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. You may want to read the post #13 after reading this post, which covers those changes.

About scene2d

Scene2d is a module of libgdx that eases the management and rendering of 2D components, which are called Actors. These actors live on a tree structure inside a container called Stage. They keep track of many rendering attributes, such as position relative 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 ship we'll fly, shots and so on. We'll use scene2d a lot in our game. Also, it's possible to apply actions on the actors, like translate, rotate, scale and fade actions. If needed, you can also write 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 user interaction by distributing touch events to the actors.

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

The following UML diagram shows it graphically:

over time. The following UML diagram shows it graphically: Using scene2d The first thing to do

Using scene2d

The first thing to do is setup a Stage where the actors will act. A nice place for it to live is within 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 class

2. Resize its viewport when the screen is resized

3. Call the Stage's act and draw methods within the screen's render method

4. 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 override methods like act() and draw(), which are automatically invoked by the stage on the appropriate time.

Modifying the Splash screen

We want the splash image to be an actor so that we can do cool things with it. Instead of

Image

extending

the

Actor

class,

we

can

just

use

the

(com.badlogic.gdx.scenes.scene2d.ui.Image) actor that comes with scene2d. We still have

to 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 the source code for the modified SplashScreen class to 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 splash image is to add a set of the following actions:

1. Fade-in in 0.75 seconds

2. Wait for 1.75 seconds

3. Fade-out in 0.75 seconds

We want also to move on to the next screen when the action is completed, so all we have

to do is add a completion listener to the "actions" object. Note that we didn't need to

override the screen's render method on the splash screen, because the splash image is an

actor 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:

I suggest you take some time to check all the available actions inside the

packagecom.badlogic.gdx.scenes.scene2d.actions, but basically there are two types of them:

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 for it to work properly. If you want to create a FadeIn action that lasts 2 seconds, you can just write:

?

1

FadeIn fadeInAction = FadeIn.$(2f);

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

?

1

2

In

?

1

And finally, add the action to the actor:

FadeOut fadeOutAction = FadeOut.$(2f);

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

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

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

?

1

actor.action(sAction);

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

Domain model

A domain model describes the entities, their attributes, roles and relationships for a given

domain of interest. Most of our business logic will stay on the domain entities, because in software engineering it's a nice idea to isolate the business logic. It makes the code straightforward to other programmers, and even to ourselves later on. It also helps when

switching technologies. Say we want to switch from 2D to 3D graphics. If the business logic

is 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 the drawable domain entities can map to a scene2d actor. That's how we'll separate the business code from the presentation code. So the next task for us is to define our domain model. In order to make it simple, we're not going to implement a full clone of Tyrian, but just the main aspects of it. After analyzing the game for some time, I came up with the following diagram:

You can check the complete source code for the domain model here.

Conclusion

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

TableLayout

Libgdx 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. You may want to read the post #13 after reading this post, which covers those changes.

Important changes

Updated the version of libgdx to the latest version available. All I had to do was download the latest build and replace the JARs and .so files on both projects (tyrian- android and 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 object through: 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 their official web- site: TWL is a graphical user interface library for Java built on top of OpenGL. It provides a rich set of standard widgets including labels, edit fields, tables, popups, tooltips, frames and a lot more. Different layout container are available to create even the most advanced user interfaces.

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

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

Some examples of TWL GUIs:

the future we can move up to TWL. Some examples of TWL GUIs: Creating the Menu
the future we can move up to TWL. Some examples of TWL GUIs: Creating the Menu

Creating the Menu Screen with scene2d

Using the TextButton (com.badlogic.gdx.scenes.scene2d.ui.TextButton) actor that comes with 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 previous post:

remember the screen flow published on a previous post: We'll use the following screen layout: 

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 to

the (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

package com.blogspot.steigert.tyrian.screens;

import com.badlogic.gdx.scenes.scene2d.Actor;

import com.badlogic.gdx.scenes.scene2d.ui.ClickListener;

import com.badlogic.gdx.scenes.scene2d.ui.Label;

import com.badlogic.gdx.scenes.scene2d.ui.TextButton;

import com.blogspot.steigert.tyrian.Tyrian;

public class MenuScreen extends AbstractScreen

{

// setup the dimensions of the menu buttons

private static final float BUTTON_WIDTH =

300f;

private static final float BUTTON_HEIGHT = 60f;

private static final float BUTTON_SPACING = 10f;

public MenuScreen(

Tyrian game )

20

super( game );

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

And the following result:

}

@Override

public void resize(

 

int width,

int height )

{

 

super.resize( width, height );

final float buttonX = ( width - BUTTON_WIDTH ) / 2;

float currentY = 280f;

// label "welcome"

Label welcomeLabel = new Label( "Welcome to Tyrian for Android!", getSkin() );

welcomeLabel.x = ( ( width - welcomeLabel.width ) / 2 );

welcomeLabel.y = ( currentY + 100 );

stage.addActor( welcomeLabel );

// button "start game"

TextButton startGameButton = new TextButton( "Start game", getSkin() );

startGameButton.x = buttonX;

startGameButton.y = currentY;

startGameButton.width = BUTTON_WIDTH;

startGameButton.height = BUTTON_HEIGHT; stage.addActor( startGameButton );

// button "options"

TextButton optionsButton = new TextButton( "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 = new TextButton( "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 );

}

}

= BUTTON_HEIGHT; stage.addActor( hallOfFameButton ); } } All right, it does the job. But is this

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.

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 GUI widgets in code is clumsy and obfuscates the widget hierarchy. TableLayout provides its own simple and concise language for expressing the GUI. TableLayout cleanly describes an 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 Editor application (Java Web-Start link).

2. Write the layout we want following the editor'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 final

configurations.

With the following layout descriptor:

?

1

2

3

4

5

6

7

8

9

I got the following preview:

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

height:60 --- [hallOfFameButton] width:300 height:60 Now we should save this layout descriptor inside our

Now we should save this layout descriptor inside our resources folder. I saved it under:

tyrian-android/assets/layout-descriptors/menu-screen.txt The final step is to modify our MenuScreen class to read this file, as follows:

?

1

2

3

4

5

6

7

8

9

10

package com.blogspot.steigert.tyrian.screens;

import com.badlogic.gdx.Gdx;

import com.badlogic.gdx.scenes.scene2d.Actor;

import com.badlogic.gdx.scenes.scene2d.ui.ClickListener;

import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.Skin;

import com.badlogic.gdx.scenes.scene2d.ui.TextButton;

import com.badlogic.gdx.scenes.scene2d.ui.tablelayout.Table;

import com.badlogic.gdx.scenes.scene2d.ui.tablelayout.TableLayout;

import com.blogspot.steigert.tyrian.Tyrian;

12

public class MenuScreen

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

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

extends

AbstractScreen

{

public MenuScreen(

Tyrian game )

{

super( game );

}

@Override

public void resize(

int width,

int height )

{

super.resize( width, height );

// retrieve the skin (created on the AbstractScreen class)

Skin skin = super.getSkin();

// create the table actor

Table table = new Table( 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 = new TextButton( "Start game", skin );

startGameButton.setClickListener( new ClickListener() {

@Override

public void click(

Actor actor, float x, float y )

{

game.setScreen( game.getStartGameScreen() );

}

} );

layout.register( "startGameButton", startGameButton );

// register the button "options"

TextButton optionsButton = new TextButton( "Options", skin );

optionsButton.setClickListener( new ClickListener() {

@Override

public void click(

 

Actor actor,

float x,

float y )

{

 

game.setScreen( game.getOptionsScreen() );

}

} );

layout.register( "optionsButton", optionsButton );

// register the button "hall of fame"

TextButton hallOfFameButton = new TextButton( "Hall of Fame", skin );

hallOfFameButton.setClickListener( new ClickListener() {

@Override

public void click(

 

Actor actor,

float x,

float y )

{

 

game.setScreen( game.getHallOfFameScreen() );

}

} );

layout.register( "hallOfFameButton", hallOfFameButton );

// finally, parse the layout descriptor layout.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 contained in the layout descriptor.

Conclusion

In this post we saw how to implement a screen layout separating the presentation (layout descriptor) from the controller (MenuScreen) code using TableLayout. Now we can easily read it and modify it when necessary. It took us some effort to learn a new component, but this effort will certainly pay off as we create and maintain the other screens. I didn't detail the Skin object we used because it's not so important at this moment, and I would need an entire post for it. This is the tag on Subversion for this post. Thanks!

Files

Libgdx version used on this post: 0.9.2 (download)

Moving on with our game development, I decided to make a slight change of plans. Instead of 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 many resource files, so it's a good idea to organize them now. I created folders for each type of resource we currently have. That is: skin, image-atlases and layout-descriptors.

Splash screen refactoring: A reader of this tutorial shared a tip on how to better use the screen's show/resize methods. In case the screen is resized, our actors don't need to be recreated, but just resized/repositioned. Here is a link containing the changes made to theSplashScreen and AbstractScreen. 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 main menu. Using the TableLayout Editor, I ended up with this layout descriptor:

?

1

2

3

4

5

6

7

8

9

debug

* padding:8

|

| fill:x

---

'High Scores' colspan:2 spacingBottom:20

---

'Episode 1'

[episode1HighScore]

---

'Episode 2'

11

---

12

'Episode 3'

13

 

---

14

15

16

17

And this preview:

[episode3HighScore]

[backButton] colspan:2 width:300 height:60 spacingTop:30

[backButton] colspan:2 width:300 height:60 spacingTop:30 Note: We talked about the TableLayout in a previous post

Note: We talked about the TableLayout in a previous post.

The code for the HighScores screen is pretty similar to the Menu screen, so I won't go into details.

The Profile domain entity

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

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

Handling preferences in libgdx

One data store available to us is the preferences mechanism. It can hold simple key-value

pairs and uses the SharedPreferences class on Android, and a flat file on Desktop. Have a look at the source code for the libgdx's Preferences interface and check out the methods it declares. This is an usage example:

?

1

2

We could do it using preferences. It's simple and effective. But let's think a little bit more.

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

prefs.putInteger( "credits", 1000 );

Some day we might release an updated version of our game. The Profile class may have changed, so we'd have to read the old preferences and updated them to the new preferences format.

There is a chance of the user to try and manually edit this preferences file in order to 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 the post #6 I wrote about the preferences mechanism.

Handling files in libgdx

The files module is another abstraction of libgdx. It lets you handle files the same way whether you're running the game on Android or on the Desktop. In the source code repository of libgdx we can also see some new backends, like GWT and iOS. So abstractions 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 a

Desktop), or inside the assets folder (on Android).

3. External: Read/Write files located in the user's home directory (in the case

of a Desktop), or in the root of the SD Card (on Android).

4. Absolute: Files referred to through absolute paths, which is normally not a

cool thing.

5. Local: Read/Write files located in the game's root directory (in the case of a

Desktop), or in the private application folder (on Android). This file type was added recently and 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 always available).

Like in Java, a directory is considered to be a File in libgdx, but instead of re-using the java.io.File class, the com.badlogic.gdx.files.FileHandleclass is used (independent of the file's type). The following lines show how to create a FileHandle for each of the available 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 with them:

Read: there are many read methods, but the simplest one is readString(), which returns 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 FileHandle with the children.

Delete: erases the file with delete(), or the directory with deleteDirectory().

Copy/Move: copies/moves the file/directory with copyTo(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 use this new file type instead of "external".

Using the JSON format

When writing to a text file we'll use the JSON notation to model our data. If you don't know JSON yet you should really take the time to read about it. JSON is gradually replacing XML since its output is easier for humans and machines to read, and the output is not so lengthy.

We'll make the Profile class know how to generate a JSON representation of its state, and also how to recover it later on. The first thing to do is make the Profile class implement com.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<String, Object> jsonData);

}

The Javadocs for the JSON classes in libgdx are missing, so reading the source code and

analysing other projects I came up with the following implementation in our Profile class:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

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

@SuppressWarnings( "unchecked" )

@Override

public void read(

Json json,

OrderedMap<String,Object> 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<String,Integer> 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 );

}

{

}

@Override

public void write(

Json json )

{

 

json.writeValue( "currentLevelId", currentLevelId );

json.writeValue( "credits", credits );

json.writeValue( "highScores", highScores );

}

The Profile service

The last piece of the puzzle is to create a service to coordinate the reading and writing of the 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 requirements for the Profile service:

The target file that will hold the state of the Profile class will be an external file located at: .tyrian/profile-v1.json; The "-v1" suffix allows us to update the file model when new versions of the game get installed.

If the target file does not exist, one should be created based on an empty Profile 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 the com.badlogic.gdx.utils.Base64Coder utility of libgdx, making it harder for players trying to edit it manually.

Should any problem occur while reading the file or restoring the Profile state, 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 this case, 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 HighScore screen, it should now retrieve the Profile through the Profile service, and simply display the high scores for each level. This is the resulting screen for all this hard work (amazing, isn't it?):

screen for all this hard work (amazing, isn't it?): Conclusion When dealing with the High Scores

Conclusion

When dealing with the High Scores screen (old Hall Of Fame screen) we learned how to use 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 some JSON utility classes. This is the tag on the Subversion for this post. Thanks for reading!

Preferences

Libgdx version used on this post: 0.9.2 (download)

Our next target is the Options screen.

About the Options screen

Once again we'll implement a very simple screen with the following options:

Sound Effects (enabled/disabled)

Music (enabled/disabled)

Volume (0-100) Using the TableLayout Editor I wrote the following layout descriptor:

?

1

2

3

4

5

6

7

8

9

debug

* padding:8

---

'Options' colspan:3 spacingBottom:20

---

'Sound Effects' [soundEffectsCheckbox] colspan:2 align:left

---

'Music'

10

11

12

13

14

15

16

Now we should think where to save these values.

[musicCheckbox] colspan:2 align:left

---

'Volume'

[volumeSlider] fill:x

[volumeValue] width:40

---

[backButton] colspan:3 width:300 height:60 spacingTop:30

Preferences mechanism

It makes no sense to store these option values in our domain model. The Profile class deals 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-value pairs to it and we don't need to care on how and where this information is going to be persisted. Seems enough.

The first thing we should do is implement a class that abstracts the access to the preferences 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

21

22

23

package com.blogspot.steigert.tyrian;

import com.badlogic.gdx.Gdx;

import com.badlogic.gdx.Preferences;

/**

* Handles the game preferences.

*/

public class TyrianPreferences

{

// constants

private static final String PREF_VOLUME = "volume";

private static final String PREF_MUSIC_ENABLED = "music.enabled";

private static final String PREF_SOUND_ENABLED = "sound.enabled";

private static final String PREFS_NAME = "tyrian";

public TyrianPreferences()

{

}

protected Preferences getPrefs()

{

return Gdx.app.getPreferences( PREFS_NAME );

24

}

25

26

public boolean isSoundEffectsEnabled()

27

{

28

return getPrefs().getBoolean( PREF_SOUND_ENABLED, true );

29

}

30

31

public void setSoundEffectsEnabled(

32

boolean soundEffectsEnabled )

33

{

34

getPrefs().putBoolean( PREF_SOUND_ENABLED, soundEffectsEnabled );

35

getPrefs().flush();

36

}

37

38

public boolean isMusicEnabled()

39

{

40

return getPrefs().getBoolean( PREF_MUSIC_ENABLED, true );

41

}

42

43

public void setMusicEnabled(

44

boolean musicEnabled )

45

{

46

getPrefs().putBoolean( PREF_MUSIC_ENABLED, musicEnabled );

47

getPrefs().flush();

48

}

49

50

public float getVolume()

51

{

52

return getPrefs().getFloat( PREF_VOLUME, 0.5f );

53

}

54

55

public void setVolume(

56

float volume )

57

{

58

59

getPrefs().putFloat( PREF_VOLUME, volume );

getPrefs().flush();

}

60

}

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 the

Desktop, this file would be saved under %USER_HOME%/.prefs/tyrian, but just when the second command gets executed. We should always flush the preferences after modifying them. On Android devices, the SharedPreferences class would be used, so all preferences are managed by the built-in preferences mechanism.

Implementing the Options screen

Before implementing the Options screen we could modify our Tyrian class to hold an instance of TyrianPreferences. Now we can finally code our screen. I won't paste the source code here because it is a bit lengthy, so I ask you to see it here. This is what we're doing:

Like the other screens we create the scene2d actors and register them in our TableLayout. 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 gets change. Basically we call our TyrianPreferences class, which abstracts the usage of the preferences mechanism. This is the resulting screen:

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

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

<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">

<properties>

<entry key="music.enabled">true</entry>

<entry key="volume">0.5</entry> <entry key="sound.enabled">true</entry> </properties>

Conclusion

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

Texture Packer

Libgdx version used on this post: 0.9.2 (download)

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

Important changes

Inside the pause method of our game (Tyrian#onPause) I'm now calling ProfileService#persist. This causes the game progress to be saved to an external file as I explained in a previous post. It's important to put this method call inside the pause method because we don't know if the player is coming back to the game after this method is executed.

I also added a flag to define the current runtime environment (development or production). This way we can change the game's default functionality to ease its development. 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 the output file, so we can easily open and edit it with a text editor. I also had to modify the code that reads the game progress to check if its contents are encoded or not.

Many changes were made to the domain entities in order to facilitate its usage. I suggest you browse each domain class using the source 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: create the layout descriptor and write the screen's code that reads it and configures the widgets.

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

OpenGL and Textures

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

In 1992 Silicon Graphics Inc. released OpenGL, an API that defines many abstractions easing the work with different graphical hardware. One of these abstractions is texture mapping. It basically states that for an image to be displayed it must come from a texture currently 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 to draw. If the image we want to show is inside another texture (other than the currently bound texture), a texture switching must occur on the GPU before OpenGL can render it. We should 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 the Java object that points to it is just a pointer, and the actual resource may be consuming the GPU's memory. The Garbage Collector doesn't know anything about the GPU, so make sure you're always disposing the textures correctly.

Image Atlas

In the previous section we learned that in order to optimize performance we should put multiple 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 the images are not related, we'll end up switching textures too many times at runtime, which is exactly what we want to avoid.

For the Splash Screen we created an image atlas of one image. That's ok because the image is large and we didn't need any other image for this screen. But when we start developing the game screen, we'll have to handle several small images. We should keep track of the dimension and location of each image inside the image atlas in order to create our TextureRegion objects later on. We could have our designer handle this, but we don't have one and event if we had, there is a better approach we can use.

Texture Packer

Libgdx provides a tool called TexturePacker that basically creates image atlases for us. All we have to do is specify where our individual images are, and where to save the output image 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, or

preferably at this 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-directories

based on the screen the images will appear. The TexturePacker uses these sub- directories to group the images, that is, images inside different sub-directories will never be placed on the same image atlas.

3. I created the image files, which you can see here.

4. I created the TyrianTexturePacker class, which calls the TexturePacker the

way we want. Basically, I told the TexturePacker to read the images from our input directory and save the output image atlases to our resource folder. Please read the

comments inside the class for detailed information.

5. I ran the TyrianTexturePacker class and refreshed my resources folder.

Texture Packer Configuration

Instead of going through each configuration option, I'll share a great link that details all the configuration that can be done. Note that the image files may follow a special nomenclature for setting specific configuration that applies only to the image itself. The underscore character is used to separate these special settings, so don't use it when naming your image files unless you want to use this feature.

As an example of that, I updated the Splash screen to also use an image atlas generated by the Texture Packer. I renamed the splash image to: splash-image_l,l.png. The "l,l" part tells 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 new TextureAtlas attribute. I

create 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 = new TextureAtlas( Gdx.files.internal( "image-atlases/pages-info" ) );

This "pages-info" file was generated by the TexturePacker, and it contains all the information of the generated image atlases.

3. When I want to load an image, all I have to do is call

getAtlas().findRegion(), passing in the name of the image file I want. This in turn

gives me an instance ofAtlasRegion, which extends TextureRegion (the class we

used previously in the Splash screen to load the splash image). Check out the source code for the modified Start Game screen for more details.

4. I modified the layout-descriptor to include the image placeholders for the

ship's items. And with the region object in hand I can just call Image#setRegion to

display the image I want. This is the final screen:

Conclusion We created the Start Game screen and learned more about OpenGL textures. We used

Conclusion

We created the Start Game screen and learned more about OpenGL textures. We used the TexturePacker tool of libgdx to automatically create image atlases for us, so we can focus on the game's mechanics. I didn't detail the source code changes too much, but I'm sure you can figure them out using the source code browser. Thanks!

Sound and Music

Libgdx version used on this post: 0.9.2 (download)

The last change before start implementing the Level screen is to add sound and music to the menu.

Important changes

I updated the libgdx JARs and SO files to the latest available.

The TyrianPreferences is now called PreferencesManager and lives inside the services package.

In the Tyrian class (our game class) the services are created inside #create().

In the AbstractScreen class I removed the disposal of the stage, since it was crashing the game in some 64-bit machines. I'm trying to understand this problem in depth, so I hope I can come up with a better solution in the future.

The ProfileService was renamed to ProfileManager, and now it uses a new file type: local (instead of external). In Android it will resolve to a folder private to the application in the local file system instead of the SD Card's root. This is good for three reasons: (1) when the application is removed, so are these files; (2) the SD Card is not always available; (3) the files are private to the application. In the Desktop it will resolve to the game's root folder.

Sound and Music

The com.badlogic.gdx.Audio interface allows us to easily handle sound and music in libgdx, and you can get a valid instance just by calling: Gdx.audio. When working with audio it's important to know that:

All sound and music should be disposed when they're no longer needed.

Sound instances are fully loaded to the heap and can have up to 1 MB.

You create sound instances through: Gdx.audio.newSound(FileHandle)

Music instances are streamed and are paused/resumed automaticaly when the game is paused/resumed.

You create music instances through: Gdx.audio.newMusic(FileHandle)

The supported formats for both sound and music are: WAV, MP3 and OGG.

The Sound and Music services

When dealing with basic audio the hard work lies on creating nice services to work with. Given the directives above it would be nice to:

Create separated sound and music services.

The music service should allow just one music to be played at any given time.

The sound service should cache the loaded sounds in order to improve performance by avoiding excessive IO reads. An LRU (Least Recently Used) cache would do the job, since we want to avoid reloading the most played sounds. After searching for some code on the web I came up with this reusable LRUCache class. I also added an eviction listener so I can dispose the sounds correctly.

Both services should manage the loaded resources, disposing them when they're no longer needed.

Both services may be turned on/off at any time, and they should respect the volume

setting. Please have a look at the source code for the MusicManager and the SoundManager. I tried to write a detailed Javadoc to ease their understanding. The Tyrian class holds references to all of our services, so I've also modified it to include our audio services. And let's not forget about the Options screen, where we can turn the audio on/off and adjust the volume.

Tyrian resources

The real Tyrian was made freeware some years ago, so it's very easy to find resources of the game on the web. I found some official sound tracks and sound effects using Google, and edited them a bit with Audacity to reduce the file sizes. We could work with lightweight MIDI files, but it's not that easy to do so because we have to create code that will run only on Android, and code that will run only on the Desktop. Also, we would have to manage them manually, so we're better off sticking to one of the supported formats.

As an example of using the audio module I modified the source code of all screens to play

a click sound when clicking on any button, and the Splash screen starts playing the menu music when it's created.

Conclusion

We learned how to use the audio module of libgdx. In case you need advanced audio

features you should try the gdx-audio extension. This is the tag for this post on the Subversion repository. On the next post we'll start to implement the game itself, so I guess

it will take a while to finish writing it. Thanks for reading!

Viewport

Libgdx version used on this post: 0.9.2 (download)

In this post we will start implementing the Levels screen by creating an actor for our ship.

Important changes

Refactorings:

Removed the preview image's attribute from the enumerated itemsShipModel, FrontGun and Shield in favour of a methodItem#getSimpleName(). With this method the advantage is that the ship's items don't contain any specific presentation information anymore. That allows us to create a convention to name our image files.

Created the LevelManager in order to make the Level domain class

dumber.

Configurations:

 

Changed the tyrian-android project so that the activity prevents the screen

from

dimming.

This

is

as

simple

as

adding a flag to the window (see

?

1

getWindow().addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON );

Creating the ship's actor

With the help of scene2d we'll create the presentation object for our ship, namely theShip2D actor. Instead of extending com.badlogic.gdx.scenes.scene2d.Actor we can rather extend com.badlogic.gdx.scenes.scene2d.ui.Image, saving us many hours of work. Its dimensions are automatically set based on the texture region's dimensions. These are the requirements for the Ship2D actor:

The actor's image should be chosen based on the ShipModel of our ship.

On the desktop the keyboard should be used to control the ship. On Android, the accelerometer should be used instead.

The ship should not be allowed to go off the screen.

When the ship moves horizontally it should tilt to the side it's moving.

The ship's acceleration/deceleration speed should start slow and increase gradually.

Presenting the ship

The first thing to do is to create a new group of images containing each of our ship models, which will be packed into an image atlas by the texture packer (for more details about this tool please have a look at the post #7). Browsing the web I found this cool image atlas, which I'll use to crop the images I want (highlighted in red):

I'll use to crop the images I want (highlighted in red): Note that I just need

Note that I just need the ship images tilting to the one side, because we can reuse these images just by flipping them horizontally (e.g., by using a negative width). We'll have to work with animations to tilt the ship, so I'll add an index to the image's name and later I'll be

able to retrieve an ordered collection of all the regions of a given image. Finally, I'll add these images to tyrian-game\etc\images\level-screen, run the texture packer and refresh my projects.

Now we can start implementing the Ship2D actor. I'll create it under the packagecom.blogspot.steigert.tyrian.screens.scene2d. In order to ease its instantiation, I'll add the following factory method:

?

1

2

3

4

5

6

7

8

9

10

11

Respecting our MVC architecture, Ship2D may know Ship but Ship may not know Ship2D. So far, so good. In the Ship2D's constructor, all we need to do is the following:

/**

*

Factory method to create a {@link Ship2D}.

*/

public static Ship2D create(

{

Ship ship, TextureAtlas textureAtlas )

List<AtlasRegion> regions = textureAtlas.findRegions(

ship.getShipModel().getSimpleName() );

Ship2D ship2d = new Ship2D( ship, regions );

return ship2d;

}

?

1

/**

2

*

Creates a new {@link Ship2D}.

3

*/

4

private Ship2D(

5

Ship ship,

6

List<AtlasRegion> regions )

7

{

8

// the super constructor does a lot of work

9

super( regions.get( 0 ) );

10

11

// set some basic attributes

12

this.touchable = false;

13

}

Finally we have to modify the Level screen to add our actor to the stage and center it horizontally, similar to what we did on the other screens. You can view the complete source code for the Level screen here. I also added a fade-in action to the whole stage (by referencing the stage's root), because the real Tyrian also does it. When starting the "Episode 1" level, we get the following screen:

the "Episode 1" level, we get the following screen: Everything is working but the ship is

Everything is working but the ship is too small, isn't it? We could create bigger images for this screen, but I want to show you another approach.

Working with the stage's viewport

We've been using a fixed resolution for the game screen and we also resize the stage to this resolution later on. But we know that Android devices may have different screen resolutions, and sooner or later we'll have to deal with that, so let's go with sooner. What we want to achieve is called resolution independence, and for that we have two options:

1. Let the stage's dimension match the current resolution and show more or

less game objects as needed.

2. Use a fixed resolution and stretch it to fit the current resolution.

In order to pick an option you have to analyze your game. Is it a problem to show more of the game for devices with greater resolutions? Will that give an unfair advantage to some players? Some games use an hybrid approach. They stretch the game without losing the aspect ratio and then they fill in the blanks with something (generally by showing more of the game).

We're going with the second option, as it is way easier than the first. But how can that increase the size of our ship? Simply put, we could define a fixed dimension for our stage's viewport in that the ship is not too small, and then we stretch the viewport to fill the current screen resolution whatever it is. So far the viewport size matched the game window's size (800x480), so if we want the ship to be two times bigger, we can set the viewport's dimensions to 400x240 without losing the aspect ratio of 1.6 we had before.

Implementing the changes

In the AbstractScreen class we need to:

Define two constants for the fixed viewport dimensions:

?

1

protected static final int VIEWPORT_WIDTH = 400, VIEWPORT_HEIGHT = 240;

Modify the instantiation of the stage object to:

?

1

this.stage = new Stage( VIEWPORT_WIDTH, VIEWPORT_HEIGHT, true );

Remove the resizing of the stage from the resize method, as we don't care about the current screen resolution anymore. And finally we should:

Remove all the overrides of the AbstractScreen#resize method.

Set the dimensions of the actors that should fill the viewport within the show method of each screen. For instance, in SplashScreen#show:

?

1

2

3

Update the layout descriptors to better fit the smaller viewport. I'll use relative dimensions on the layout descriptor, so if we change the fixed dimensions of the viewport again there is a chance the layouts won't break. You can view the modified layout descriptors here. When I run the Desktop launcher on a 800x480 window I get the following screens:

splashImage = new Image( splashRegion, Scaling.stretch );

splashImage.width = stage.width();

splashImage.height = stage.height();

The ship is now bigger, but the start game screen is now broken. We should
The ship is now bigger, but the start game screen is now broken. We should
The ship is now bigger, but the start game screen is now broken. We should

The ship is now bigger, but the start game screen is now broken. We should refactor this screen, creating other screens for each customizable item, but as we're playing with the viewport, we could increase its dimensions just for the menu screens. So this is the plan:

All the menu screens should have a viewport of 800x480.

The level screen should have a viewport of 400x240. You can browse the source code for the modified screens here. Now I suggest you play with the game window's size, either by resizing it manually or using specific values on TyrianDesktopLauncher. As you do that don't worry if the screen's contents are ugly. On Android they look just nice.

Conclusion

We've finally started implementing the game by creating the Ship2D actor. We played with the stage's viewport to increase the ship's size, and ended up discussing about how to

achieve resolution independence. In the next post we'll handle the ship's requirements not yet implemented. Thanks for reading!

Accelerator and Keyboard

Libgdx version used on this post: 0.9.2 (download)

First of all, a big thanks to my first supporter! A german guy bought me a beer by donating some money. Vielen Dank! Mit diesem Geld kaufe ich ein deutsches Weizenbier!

Important changes

Updated the ADT Plugin on Eclipse to version 17. It seems that every JAR that's inside the "libs" folder of an Android project gets added to its classpath automatically. When testing I also noticed that tyrian-game was not being sent to the Android device, so the game crashed because of a ClassNotFoundException. This is what I did to fix the problems:

1. In tyrian-android, I removed all the JARs from the classpath and let

Android reference them through the new Android Dependencies classpath item.

2. Exported the "gdx.jar" item in Properties > Java Build Path > Order and

Exportof tyrian-game.

3. Exported the tyrian-game item in Properties > Java Build Path > Order

and Export of tyrian-android. More details about libgdx on ADT v17 can be found here.

Moving the ship

When rendering a screen (AbstractScreen#render method) we have to first process the game logic and then draw the result. In the first part we can check for user input and modify our actors accordingly. As we want to move the ship, a common approach is to use the device's accelerometer as an input, but we should also support a keyboard because we're testing the game on the Desktop.

We'll start by adding a moveShip(delta) method to Ship2D. This method will take care of

all the movement details, like making sure the ship is inside the screen, calculating its variable movement speed and deciding whether to use a keyboard or the accelerometer. Notice the method's delta parameter. With this information in hand we can define a maximum movement speed for the ship whose unit is pixels per second. We could start off setting some speed constant and adjusting it as needed. For instance:

?

1

private static final float MAX_MOVEMENT_SPEED = 250;

This means the ship can move 250 pixels per second. Our fixed game viewport is sized

400x240, so in practice the ship will take 400/250 (=1.6) seconds to fly from the left to the right edges of the screen, and 240/250 (=0.96) seconds from the bottom to the upper edges. We could go on with this approach, but let's say we change the fixed viewport's size in the future in order to deliver higher resolution graphics. The movement speed would need to be reviewed because of this. So instead, let's just use the time we want the ship to travel between the edges of the screen.

?

1

private static final float MAX_HORIZONTAL_SPEED = ( AbstractScreen.GAME_VIEWPORT_WIDTH / 1.6f );

private static final float MAX_VERTICAL_SPEED = ( AbstractScreen.GAME_VIEWPORT_HEIGHT / 0.96f );

2

Later we can adjust these constants as we test the game on a real device.

Using the Keyboard

No secrets here. I'll just check if the arrow keys are pressed and move the ship accordingly.

?

1

2

3

4

5

6

7

if( Gdx.input.isKeyPressed( Input.Keys.UP ) ) y += (

MAX_VERTICAL_SPEED * delta ); else if( Gdx.input.isKeyPressed( Input.Keys.DOWN ) ) y -= ( MAX_VERTICAL_SPEED * delta );

if( Gdx.input.isKeyPressed( Input.Keys.LEFT ) ) x -= (

MAX_HORIZONTAL_SPEED * delta );

else if( Gdx.input.isKeyPressed( Input.Keys.RIGHT ) ) x += (

MAX_HORIZONTAL_SPEED * delta );

Using the accelerometer

We can check if an accelerometer is present with the following call:

?

1

Gdx.input.isPeripheralAvailable( Peripheral.Accelerometer )

With that we can decide which input mechanism to use. On Android we should not forget to

declare the accelerometer requirement on the AndroidManifest.xml. It is just informational, but the app stores will use it to filter the supported applications for a given device.

?

1

2

3

And the following calls retrieve the accelerometer's current data:

<uses-feature

android:name="android.hardware.sensor.accelerometer"

android:required="true" />

?

1

Gdx.input.getAccelerometerX(); // points to the right (when in portrait orientation)

2

Gdx.input.getAccelerometerY(); // points upwards (when in portrait orientation)

3

Gdx.input.getAccelerometerZ(); // points to the front of the display (coming out of the screen)

Each retrieved value ranges from -10 to 10, but we should come up with specific ranges

that suit our game. In order to do that, I added the following code to the moveShip method:

?

1

2

3

4

5

6

Then I executed the game on my device, decided the best ways to tilt the phone to move the ship and noted down the values of the accelerometer. Notice that as the game is in landscape mode, our game's x-axis corresponds to the accelerator's y-axis. Here they are:

if( Tyrian.DEV_MODE ) {

Gdx.app.debug( Tyrian.LOG,

Gdx.input.getAccelerometerX() + "," +

Gdx.input.getAccelerometerY() + "," +

Gdx.input.getAccelerometerZ() );

}

Horizontal movement (accelerator's y-axis):

[-10,-2]: moving left at maximum speed

(-2,0): moving left at calculated speed

0: still

(0,2): moving right at calculated speed

[2,10]: moving right at maximum speed

Vertical movement (accelerator's x-axis):

[-10,0]: moving forward at maximum speed

(0,2): moving forward at calculated speed

2: still

(2,4): moving back at calculated speed

[4,10]: moving back at maximum speed Given that, I can use the accelerometer's values to modify the ship's position on the stage:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// x: 4 (back), 2 (still), 0 (forward)

// I'll translate the above values to (-2,0,2) so that my next calculations are simpler

float adjustedX = ( Gdx.input.getAccelerometerX() - 2f );

if( adjustedX < - 2f ) adjustedX = - 2f; else if( adjustedX > 2f ) adjustedX = 2f;

// y: -2 (left), 0 (still), 2 (right)

float adjustedY = Gdx.input.getAccelerometerY();

if( adjustedY < - 2f ) adjustedY = - 2f; else if( adjustedY > 2f ) adjustedY = 2f;

// since 2 is 100% of movement speed, let's calculate the final speed percentage

adjustedX /= 2;

adjustedY /= 2;

// notice the inverted axis because the game is displayed in landscape mode

x += ( adjustedY * MAX_HORIZONTAL_SPEED * delta );

y += ( - adjustedX * MAX_VERTICAL_SPEED * delta );

Making sure the ship is inside the screen

We don't want the ship to go off the screen, right? So all we have to do is check the ship's position against the stage's dimensions. The following code does the job:

?

1

if( x < 0 ) x = 0; else if( x > stage.width() - width ) x = stage.width() - width;

2

if( y < 0 ) y = 0; else if( y > stage.height() - height ) y = stage.height() - height;

Notice that I used the ship's width and height because an actor's origin stays at the bottom left corner.

You can view the complete source code for the Ship2D class here.

Conclusion

The ship is now able to fly, but there are still two problems to solve:

1. The ship has no acceleration (it responds to commands immediately).

2. The ship is not tilting to the side it's moving.

On the next post we'll handle that. Here is the Subversion repository tag for this post. Thanks!

Vectors

Libgdx version used on this post: 0.9.3 (download)

Hi guys! I've been extremely busy with some parallel projects, but as I received some donations and amazing feedbacks as well, I'll find some time to write more posts! In this one we'll talk about vectors, an element that allows us to represent positions, speeds, velocities, accelerations, directions, distances and so on.

Important changes

Updated the version of libgdx to the latest version available. All I had to do was to download the latest build and replace the JARs and .so files in our projects.

Why should we use vectors?

Vectors are cool because they can represent some information in a way that's easy to understand, handle and combine. Basically they represent a direction or a point within a coordinate system, optionally with an associated scalar value.

So far we've dealt only with velocity, but we also want to add acceleration so that the ship's movement seems more real. Using vectors we can easily calculate the final position of the ship after both forces were applied just by combining them! If you want to study the vector concept in depth you can check out