2012-07-09

#13 - libgdx Tutorial: Libgdx refactoring (version 0.9.6)

Libgdx version used on this post: 0.9.6 (download)
As libgdx evolves we'll need to update our code base to make use of the latest improvements. The following posts were published on the official libgdx blog: On this post I'll detail the most important changes. The first thing we should do is download the latest version of libgdx and replace the JAR and SO files in our projects. After doing that you'll notice that many compile errors will appear.

Important changes in scene2d

Getters and setters added
Using getters and setters is a simple best practice as it improves the encapsulation of behavior, but it could hinder the performance because these methods will be called several times for each rendered frame. I suggest you keep an eye on the FPS output of your game. For Tyrian it won't make a difference. If you'd like to read more about the advantages of using getters and setters, have a look at this question at stackoverflow.

Actions system rewritten
It's now even easier to combine actions because they can operate on the same properties of an actor. The syntax has also changed a bit, so instead of FadeIn.$(0.75f) we can statically import the action and use fadeIn(0.75f). The actions you can statically import can be found in com.badlogic.gdx.scenes.scene2d.actions.Actions.

Previously we did this in SplashScreen:
Sequence actions = Sequence.$( FadeIn.$( 0.75f ), Delay.$( FadeOut.$( 0.75f ), 1.75f ) );
splashImage.action( actions );
And now we do:
splashImage.addAction( sequence( fadeIn( 0.75f ), delay( 1.75f ), fadeOut( 0.75f ) ) );
Generic event system developed
Each actor has a list of capture and event listeners. Both listeners extend the generic com.badlogic.gdx.scenes.scene2d.EventListener. The capture listeners may intercept the events before they're handled by the event listeners. During this capture phase, the event is given to the root down to the target actor. If the event is not cancelled by a capture listener, the normal phase kicks in and the event is given to the target actor up to the stage's root. Given that, the following is possible:
  • Instead of querying the input mechanism to move our ship, we could just receive the input events as they happen and modify our actor accordingly (see com.badlogic.gdx.scenes.scene2d.ActorListener). I didn't see any event related to the accelerometer though, so let's stick to what we were doing;
  • We can also fire our own events. I'm glad to see this feature, otherwise I'm pretty sure we'd have to implement it ourselves;
  • We can create a hierarchy of event listeners, possibly extending one of the shipped listeners:
    • ActorListener: listens for input events, like keyDown/Up/Typed, enter/exit, touchUp/Down and so on;
    • ChangeListener: listens for change events, that is, when something changed in an actor;
    • ActorGestureListener: makes it easy to work with gesture events, like pinch, zoom and fling.
  • We can reuse event listeners by adding them to different actors.

Actions and the new event system
I was expecting to receive events as the actions started/stoped on some actor, but sadly it doesn't work that way. In order to switch from the SplashScreen to the MenuScreen, we used to do something like this:
 Sequence actions = Sequence.$( FadeIn.$( 0.75f ), Delay.$( FadeOut.$( 0.75f ), 1.75f ) );
 actions.setCompletionListener( new OnActionCompleted() {
  public void completed( Action action ) {
   game.setScreen( new MenuScreen( game ) );
  }
 } );
 splashImage.action( actions );
Now that OnActionCompleted was removed, we can do this little trick:
 splashImage.addAction( sequence( fadeIn( 0.75f ), delay( 1.75f ), fadeOut( 0.75f ),
  new Action() {
   public boolean act( float delta ) {
    game.setScreen( new MenuScreen( game ) );
    return true; // returning true consumes the event
   }
  } )
 );
The Drawable interface
An abstraction was created to handle objects that know how to draw themselves: com.badlogic.gdx.scenes.scene2d.utils.Drawable. The following list shows all the currently shipped drawables, but of course you can also write your own:
  • EmptyDrawable: A good drawable to extend if you're planning to write custom drawables. Provides a rectangular drawable area but draws nothing.
  • NinePatchDrawable: Wraps a NinePatch, which provides dynamic image stretching. You can create nine patches with Android's draw9patch tool;
  • TextureRegionDrawable: Wraps a TextureRegion, which is a rectangular area of a Texture;
  • SpriteDrawable: Wraps a Sprite, which describes both a texture region, the geometry where it will be drawn, and the color it will be drawn.

Animations and the new Drawable interface
Remeber we use a 2D animation to tilt the ship? The utility class we used (com.badlogic.gdx.graphics.g2d.Animation) still requires TextureRegions for the animation's frames. The problem is that the modified Image actor now requires a Drawable, and not a TextureRegion. So we'd better create a map whose keys are TextureRegions and the values, Drawables. This way we can avoid instantiating Drawables on demand, which would wake up the garbage collector sometimes, resulting in small freezes while playing the game. Have a look at the modified com.blogspot.steigert.tyrian.screens.scene2d.Ship2D code for detailed information.

Utility classes added
Utility classes are great, aren't they? This list highlights some of them:
  • Pool and Poolable: A pool of objects that can be reused to avoid allocation;
  • ReflectionPool: A customization of pool that uses reflection to build instances;
  • Timer: Schedules the execution of tasks in a platform independent manner;
  • Array: A resizable, ordered/unordered array of objects;
  • DelayedRemovalArray and SnapshotArray: Customizations of Array that handle concurrent modification in specific ways;
I suggest you take some time to view all classes under com.badlogic.gdx.utils. They can really save you hours of work.

Documentation is being rewritten
It already contains many useful information (check it out), but meanwhile I make millions of dollars with this blog. Have you clicked the ad banner today? :)

The new TexturePacker

The TexturePacker was completely rewritten. It's now said to be much faster, easier to use and most importantly, it packs images better. A new packing algorithm is being used, the MAXRECTS, created by Jukka Jylänki. Here is the official documentation for the updated version. From the related libgdx post:
  • Configuration: The underscore file name conventions are gone. File names can still have a numeric index, it is simply any numbers at end of the file name, before the file extension. To better control packing in various ways, a "pack.json" file can be placed in each input directory. This contains the JSON for the com.badlogic.gdx.tools.imagepacker.TexturePacker2$Settings object. Each directory inherits all settings from the parent directory and any property set here will override those.
  • Nine patches: Files with ".9" before the file extension are considered a nine patch file. The pixels around the edges are read and stripped off before packing the image. Once again, you can create nine patches with Android's draw9patch tool. The split information is stored in the pack file, enabling TextureAtlas to provide instances of NinePatch. The splits in the TextureAtlas file format are optional, so existing pack files are still compatible.

In Tyrian we'll add the pack.json file in tyrian-game/etc/images, and this file will hold the configuration values for the TexturePacker. You can play with some properties if you want to customize the output. The only non-default values I used were the "filterMin" and "filterMag", which I set to "Linear". I also removed the configuration directives from the raw images' names (e.g.: splash-image_l,l.png is now splash-image.png). To run the TexturePacker, we could execute the following command on the prompt:
java -classpath gdx.jar;gdx-tools.jar com.badlogic.gdx.tools.imagepacker.TexturePacker2 inputDir outputDir packFileName
But we can do it inside Eclipse by creating a Java launcher. I saved it under /tyrian-game/TyrianTexturePacker2.launch, so you can open it with your Eclipse like so: Run > Run Configurations... > Java Application > TyrianTexturePacker2.
That done we can use the TextureAtlas class like this:
TextureAtlas atlas = new TextureAtlas(Gdx.files.internal("image-atlases/pages.atlas"));
AtlasRegion region = atlas.findRegion("image-name");
Sprite sprite = atlas.createSprite("image-name");
NinePatch patch = atlas.createPatch("image-name");

Important changes in Table Layout

Remember our layout descriptors? Well, they're gone. The main argument for its removal was that most of the time we "have to fallback to the code to manage the UI". As I see, the controller (screen) will be even more coupled to the view. I'm sad with this change because I like to have the layout structure in separate files, but let's follow the updated TableLayout's documentation and modify our code. Now we have to do something like this:
// creates the table actor
Table table = new Table();
// 100% width and 100% height on the table (fills the stage)
table.setFillParent(true);
// add the table to the stage
stage.addActor(table);
// add the welcome message with a margin-bottom of 50 units
table.add( "Welcome to Tyrian for Android!" ).spaceBottom( 50 );
// move to the next row
table.row();
// add the start-game button sized 300x60 with a margin-bottom of 10 units
table.add( startGameButton ).size( 300f, 60f ).uniform().spaceBottom( 10 );
// move to the next row
table.row();
// add the options button in a cell similiar to the start-game button's cell
table.add( optionsButton ).uniform().fill().spaceBottom( 10 );
// move to the next row
table.row();
// add the high-scores button in a cell similiar to the start-game button's cell
table.add( highScoresButton ).uniform().fill();
I removed the layout descriptors and updated all the screens to comply with this new API, and you can see the result below:

Conclusion

Libgdx is not yet mature, so we should expect future refactorings like this one. But it's evolving quite fast and it already provides many features required for commercial game projects. This is the tag on the Subversion repository for this post, and I these are the commit differences from the previous revision.
Thanks for reading!

2012-06-25

#12 - libgdx Tutorial: 2D Animations

Libgdx version used on this post: 0.9.4 (download)
Like in many other gaming frameworks, a 2D animation is just a sequence of static images (frames) shown with a certain speed, creating an illusion of movement. On this post we'll use an animation to make the ship tilt to the side it's moving.

Important changes made to the existing code:

  • Enabled the use of OpenGL ES 2.0 in the both launchers: TyrianAndroidLauncher and TyrianDesktopLauncher. Libgdx will fallback to OpenGL ES 1.x in case the 2.0 isn't available. This can bring your game some valuable performance enhancements as stated here (section: Choosing an OpenGL API Version).
  • The Ship2D class was modified as follows:
    • We'll be using a simple MAX_SPEED constant (given in pixels/second), instead of the previous MAX_HORIZONTAL_SPEED and MAX_VERTICAL_SPEED.
    • The moveShip method was simplified regarding its interpretation of the accelerometer values. I also created a VectorUtils class to remove many ifs from this method, making it easier to read.
    • A deceleration was added to make the ship stop flying when there is no user input. I won't go into details on this because on the previous post I wrote about acceleration, so you should be able to easily understand this modification. If you have any questions, just post it as a comment.

Using animations

In libgdx we use the com.badlogic.gdx.graphics.g2d.Animation class to perform 2D animations. This class holds the animation's frames (in the form of TextureRegion instances) and sets the time each frame should be shown. This utility class performs no drawing at all. After it's created, we'll make use of the following method:
public TextureRegion getKeyFrame(float stateTime, boolean looping)
This method retrieves the next frame to be drawn. Let's understand each parameter:
  • float stateTime: this is an accumulator value that represents the time elapsed since the start of the animation. We should store this value somewhere and add the delta time to it before calling the method.
  • boolean looping: whether the animation loops or stops at the last frame.
In order to create an Animation instance we must supply the TextureRegion instances and the frame duration in seconds. As we're using the Texture Packer to create our image atlases, we can use our TextureAtlas instance to easily find all the frames the compose an animation just by calling textureAtlas.findRegions( "animation-name" ), which returns a list of AtlasRegion instances, which extend TextureRegion.
Note: we talked about the Texture Packer and image atlases in a previous post.
You can test and tune the frame duration value or come up with some formula, like: 2.5 / frameCount
That means the whole animation lasts 2.5 seconds. Here you can read more about animations in libgdx if you want.

Scene 2D and animations

Scene 2D has no animation actor, but that's not a problem because we can just change our image actor's region based on the TextureRegion returned by the Animation class. Simply put, we can do something like this:
animationStateTime += delta;
TextureRegion frame = animation.getKeyFrame( animationStateTime, false );
setRegion( frame );

Creating the tilt animation

The Texture Packer created the following image atlas for us:


Notice that we just have the tilt animation to the left. Later we'll use a trick to inverse the image horizontaly, so the ship will be able to tilt to both sides. Let's define the new animation related attributes:
    /**
     * The ship's tilt animation.
     */
    private final Animation tiltAnimation;

    /**
     * The ship's tilt animation's state time.
     */
    private float tiltAnimationStateTime;
In the constructor we create the tilt animation:
this.tiltAnimation = new Animation( 0.15f, tiltAnimationFrames );
But were these frames come from? In our factory method (Ship2D#create) we receive a TextureAtlas instance. So inside the factory method we can just call:
 List<AtlasRegion> regions = textureAtlas.findRegions( ship.getShipModel().getSimpleName() );
Ship2D ship2d = new Ship2D( ship, regions );
There is a small problem with this approach. Both the level screen and the start game screen have images for the ship, and as we use a convention to name our images, the retrieved regions contain the ship's image of the start game screen. This image is not a part of the tilt animation. We could just rename the images in order to avoid this conflict (and this is the best fix in my opinion), but there is a trick we can do. The Texture Packer stores the image's index when it's part of an animation. If the index is less than zero it's just an static image. The following code removes the undesired ship's image:
 List<AtlasRegion> regions = textureAtlas.findRegions( ship.getShipModel().getSimpleName() );
 Iterator<AtlasRegion> regionIterator = regions.iterator();
 while( regionIterator.hasNext() ) {
  if( regionIterator.next().index < 0 ) {
   regionIterator.remove();
  }
 }

Tilting the ship based on user input

We're almost done. All we have to do now is analyse the user input and tilt the ship. We're better off creating a specific method for that, so now our Ship2D#act method now looks like this:
    @Override
    public void act( float delta )
    {
        super.act( delta );
        moveShip( delta );
        tiltShip( delta );
    }
And now let's add the tiltShip method. Please analyse the code below and read the inline comments:
    /**
     * Tilts the ship to the direction its moving.
     */
    private void tiltShip(
        float delta )
    {
        // the animation's frame to be shown
        TextureRegion frame;

        // find the appropriate frame of the tilt animation to be drawn
        if( velocity.x < 0 ) {
            frame = tiltAnimation.getKeyFrame( tiltAnimationStateTime += delta, false );
            if( frame.getRegionWidth() < 0 ) frame.flip( true, false );
        } else if( velocity.x > 0 ) {
            frame = tiltAnimation.getKeyFrame( tiltAnimationStateTime += delta, false );
            if( frame.getRegionWidth() > 0 ) frame.flip( true, false );
        } else {
            tiltAnimationStateTime = 0;
            frame = tiltAnimation.getKeyFrame( 0, false );
        }

        // there is no performance issues when setting the same frame multiple
        // times as the current region (the call will be ignored in this case)
        setRegion( frame );
    }
Important aspects of the above method:
  • Based on the velocity vector we can tell if the ship is moving horizontaly.
  • We always increment the tiltAnimationStateTime variable with the given delta before requesting the next animation's frame to be drawn.
  • We flip the image horizontaly when needed. A positive width means the image is not inverted (it's being displayed as it is). A negative width means the image is inverted horizontaly.
  • If the ship is not moving horizontaly we reset the tiltAnimationStateTime variable and set the current frame to the first frame.

The following video shows the result:



Conclusion

The ship is now tilting to the side it's moving. We used some tricks to get this done, but the code is still readable. :) This is the Subversion tag for this post, and this is the full source code for the Ship2D class. Thanks for reading!

2012-05-28

#11 - libgdx Tutorial: 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 informations 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 this Wikipedia page.

With a vector in hands we can do the following operations:
  • Add/subtract other vector to/from it;
  • Multiply it by a scalar value;
  • Calculate its length, which gives us the associated scalar value;
  • Normalize it, which makes its length be 1;
  • Calculate its angle relative to the x-axis;
  • Rotate it by a given angle;
  • Calculate its distance to another vector.

Libgdx provides two vector implementations:
  • com.badlogic.gdx.math.Vector2 for 2D vectors (javadoc)
  • com.badlogic.gdx.math.Vector3 for 3D vectors (javadoc)

As you can see, they belong to the com.badlogic.gdx.math package, which is very useful. It provides implementations of things like circles, planes, polygons, rectangles, spheres and so on. I suggest you take some time to study this package in details. Maybe one day I'll write about it.

Using a position vector

As an example, we will change our game to use a vector to represent the ship's position. Let's start by modifying the Ship2D class to include the following vector attribute:
    private Vector2 position; // unitless
Sadly the (x,y) position coordinates of an actor are not wrapped into a Vector2 object. That's why we're using a position vector. At specific moments we'll have to update the actor's position based on our position vector. Now we could rewrite our Ship2D#moveShip method as follows (I omitted some code in favor of readability):
    private void moveShip(
        float delta )
    {
        // set the position vector to the ship's current position
        position.set( x, y );

        // move UP or DOWN
        if( Gdx.input.isKeyPressed( Input.Keys.UP ) ) {
            position.add( 0, MAX_VERTICAL_SPEED * delta );
        } else if( Gdx.input.isKeyPressed( Input.Keys.DOWN ) ) {
            position.sub( 0, MAX_VERTICAL_SPEED * delta );
        }

        // move LEFT or RIGHT
        if( Gdx.input.isKeyPressed( Input.Keys.LEFT ) ) {
            position.sub( MAX_HORIZONTAL_SPEED * delta, 0 );
        } else if( Gdx.input.isKeyPressed( Input.Keys.RIGHT ) ) {
            position.add( MAX_HORIZONTAL_SPEED * delta, 0 );
        }

        // update the ship's actual position
        x = position.x;
        y = position.y;
    }

Using velocity and acceleration vectors

We're using vectors now, but the result was exactly the same as before! When we add the acceleration vector we'll see all the magic happen. First, let's understand the difference between speed, velocity and acceleration:
  • Speed is a scalar quantity that tells us how fast an object is moving; eg: 10m/s.
  • Velocity is a vector quantity that tells us how fast an object is moving and in what direction; eg: 10m/s east.
  • Acceleration is a vector quantity that tells us the rate of change of velocity; eg: 10m/s² south.
So what we need to do now is:
  1. Calculate the acceleration based on the user input.
  2. Modify the ship's velocity based on the calculated acceleration.
  3. Update the ship's position based on the modified velocity.
And we should enforce some rules:
  • The maximum acceleration will be 8px/s². When using the keyboard, this maximum value is reached immediately, but when using the device's accelerator it should be calculated.
  • The maximum speeds (horizontal and vertical) must be respected.
  • When reaching the boundaries, the ship's velocity should be zeroed, so that the ship stops flying in the current direction.

Modifying the Ship2D#moveShip method

We need to set the maximum acceleration, just like we set the maximum speed:
    private static final float MAX_ACCELERATION = 8; // unit: px/s²
And we need the new vectors:
    private Vector2 velocity; // unit: px/s
    private Vector2 acceleration; // unit: px/s²
Now, let's follow our plan:

1) Calculate the acceleration based on the user input
The acceleration should be set as follows:
  • User is pressing the left arrow key: acceleration.x = - MAX_ACCELERATION
  • User is pressing the right arrow key: acceleration.x = MAX_ACCELERATION
  • User is pressing the up arrow key: acceleration.y = MAX_ACCELERATION
  • User is pressing the down arrow key: acceleration.y = - MAX_ACCELERATION
We must not forget to adjust the value based on the current delta!
  • acceleration = acceleration * delta

2) Modify the ship's velocity based on the calculated acceleration
That's easy:
  • velocity = velocity + acceleration
  • check the max speed

3) Update the ship's position based on the modified velocity
That's easy too now that we have vectors:
  • position = position + velocity
  • check the boundaries

The result

Once again, I'll just detail the keyboard input here because it's simpler. Later you can check out the full source code for the Ship2D class with detailed inline comments.
    private void moveShip( float delta )
    {
        // calculate the horizontal and vertical acceleration
        acceleration.x = ( Gdx.input.isKeyPressed( Input.Keys.LEFT ) ? - MAX_ACCELERATION
            : ( Gdx.input.isKeyPressed( Input.Keys.RIGHT ) ? MAX_ACCELERATION : 0 ) );
        acceleration.y = ( Gdx.input.isKeyPressed( Input.Keys.UP ) ? MAX_ACCELERATION
            : ( Gdx.input.isKeyPressed( Input.Keys.DOWN ) ? - MAX_ACCELERATION : 0 ) );

        // note that when the keys are not pressed, the acceleration will be
        // zero, so the ship's velocity won't be affected

        // apply the delta on the acceleration
        acceleration.mul( delta );

        // modify the ship's velocity
        velocity.add( acceleration );

        // check the max speed
        if( velocity.x < 0 ) {
            velocity.x = Math.max( velocity.x, - MAX_HORIZONTAL_SPEED * delta );
        } else if( velocity.x > 0 ) {
            velocity.x = Math.min( velocity.x, MAX_HORIZONTAL_SPEED * delta );
        }
        if( velocity.y < 0 ) {
            velocity.y = Math.max( velocity.y, - MAX_VERTICAL_SPEED * delta );
        } else if( velocity.y > 0 ) {
            velocity.y = Math.min( velocity.y, MAX_VERTICAL_SPEED * delta );
        }

        // update the ship's position
        position.add( velocity );

        // make sure the ship is inside the stage
        if( position.x < 0 ) {
            position.x = 0;
            velocity.x = 0;
        } else if( position.x > stage.width() - width ) {
            position.x = stage.width() - width;
            velocity.x = 0;
        }
        if( position.y < 0 ) {
            position.y = 0;
            velocity.y = 0;
        } else if( position.y > stage.height() - height ) {
            position.y = stage.height() - height;
            velocity.y = 0;
        }

        // update the ship's actual position
        x = position.x;
        y = position.y;
    }

Conclusion

We have implemented acceleration and the ship's behaviour seems more real! Features like these are absolutely necessary if we want the player to be able to immerse in the game. In the next post the ship will tilt to the side its moving, and then we'll move on to another actors!

Here is the Subversion repository tag for this post. Thanks!