LibGDX Tutorial 3: Basic graphics

This is the part people always find the most fun, the actual act of putting graphics up on screen.  Let’s start with about the simplest project that we can.

We are going to display this sprite (created in this tutorial):

jet

On screen.  One important thing to note, the above graphic is 512×256.  OpenGL in general and LibGDX in specific, require your image files to be in power of two dimensions.  This means your width and height are 2,4,8,16,32,64,128,256,512,1024,2048, etc… pixels in size.  Be sure to add this file to the assetsdata folder in the android project before continuing.

Let’s jump right in with code:

package com.gamefromscratch.graphicsdemo;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class GraphicsDemo implements ApplicationListener {
    private SpriteBatch batch;
    private Texture texture;
    private Sprite sprite;
    
    @Override
    public void create() {        
        batch = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("data/jet.png"));
        sprite = new Sprite(texture);
    }

    @Override
    public void dispose() {
        batch.dispose();
        texture.dispose();
    }

    @Override
    public void render() {        
        Gdx.gl.glClearColor(1, 1, 1, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        batch.begin();
        sprite.draw(batch);
        batch.end();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

And if you run it:

image

The image is drawn relative to the origin.  In the case of LibGDX (0,0) is the bottom left corner of the screen.

As to the code, there isn’t actually a ton new compared to the Hello World example in the previous tutorial.  The only new concepts are the Texture and the Sprite.  The texture represents the underlying OpenGL texture.  One important thing to keep in mind with Texture ( and other similar classes ) is they implement the Disposable interface.  This means when you are done with it, you have to call the dispose() method, or you will leak memory!  A Sprite holds the geometry and colour data of a texture, this means the positional data ( such as it’s X and Y location ) are stored in the Sprite.  We construct our texture by passing it’s path in, obtained in the same manner we access the font in the prior tutorial.  We then construct the Sprite by passing in our newly created texture.  There are other ways of creating Sprites, that we will see shortly.  Just like in the Hello World sample, we start a SpriteBatch, and draw our sprite to it using the draw() method.

Dynamic textures with Pixmap

Your Texture’s source doesn’t have to come from a file.  Here we are going to use the Pixmap class to create the texture’s source dynamically.

package com.gamefromscratch.graphicsdemo;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class GraphicsDemo implements ApplicationListener {
    private SpriteBatch batch;
    private Pixmap pixmap;
    private Texture texture;
    private Sprite sprite;
    
    @Override
    public void create() {        
        batch = new SpriteBatch();
        
        // A Pixmap is basically a raw image in memory as repesented by pixels
        // We create one 256 wide, 128 height using 8 bytes for Red, Green, Blue and Alpha channels
        pixmap = new Pixmap(256,128, Pixmap.Format.RGBA8888);
        
        //Fill it red
        pixmap.setColor(Color.RED);
        pixmap.fill();
        
        //Draw two lines forming an X
        pixmap.setColor(Color.BLACK);
        pixmap.drawLine(0, 0, pixmap.getWidth()-1, pixmap.getHeight()-1);
        pixmap.drawLine(0, pixmap.getHeight()-1, pixmap.getWidth()-1, 0);
        
        //Draw a circle about the middle
        pixmap.setColor(Color.YELLOW);
        pixmap.drawCircle(pixmap.getWidth()/2, pixmap.getHeight()/2, pixmap.getHeight()/2 - 1);
        
        
        texture = new Texture(pixmap);
        
        //It's the textures responsibility now... get rid of the pixmap
        pixmap.dispose();
        
        sprite = new Sprite(texture);
    }

    @Override
    public void dispose() {
        batch.dispose();
        texture.dispose();
    }

    @Override
    public void render() {        
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        batch.begin();
        sprite.setPosition(0, 0);        
        sprite.draw(batch);
        sprite.setPosition(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2);
        sprite.draw(batch);
        batch.end();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

Once again, the code is remarkably similar to our prior example.  The biggest difference is instead of loading the textures image data from file, we create one dynamically using a Pixmap.  At the simplest, a pixmap can be thought of as a grid of pixel data in memory.  It contains a number of graphical functions, many of which we demoed above.  The drawing code is pretty well commented in terms of what it does, so I wont go into details here.  One very important detail though, in the modern GPU driven world, these kinds of per pixel operations are REALLY REALLY SLOW.  Generally you want to avoid them as much as possible. 

The only other thing of note in this example is the changes in the render() method.  Notice how the same sprite is drawn twice to the sprite batch?  Well this behaviour is perfectly OK, and has minimal performance overhead in doing so.  Sprite’s setPosition method is used to position a sprite, once again, (0,0) is the lower left hand corner of the screen by default.  The only other new code here is the Gdx.graphics.getWidth() and getHeight() method calls.  These return the window’s ( or Canvas in the case of HTML5 ) dimensions.  Of course in production code you would probably cache them locally instead of retrieving them every pass through the render loop.

TextureAtlas

Quite often you want to deal with a sprite sheet, which is a number of sprites combined together into a single image.  Such functionality is built into LibGdx.  The first thing you are going to need is a directory of images that are going to be combined into a sprite sheet.  Like this:

image

Open a command line or terminal window and run the following command:

java -cp gdx.jar;extensions/gdx-tools/gdx-tools.jar com.badlogic.gdx.tools.imagepacker.TexturePacker2 c:tmp c:tmp spritesheet
tmp

It looks more unwieldy than it is.  Basically you are running the TexturePacker2 class inside the gdx-tools jar.  The first parameter is the source directory, the second parameter is the destination direction and the final parameter is the filename to use.  It will automatically add the required file extensions.  This process will create two files, a .atlas file and a .png.  The atlas file is a text file describing how the sprites are laid out in the spritesheet image, with the images filename used as the key ( minus extension ), like so:

spritesheet.atlas:

spritesheet.png
format: RGBA8888
filter: Nearest,Nearest
repeat: none
0001
  rotate: false
  xy: 1, 651
  size: 192, 128
  orig: 192, 128
  offset: 0, 0
  index: -1
0002
  rotate: false

While the sprite sheet itself looks like this:

spritesheet

The spritepacker tool automatically pads the image out to be a power of 2 in size.  I’ve only scratched the very surface of what this tool can do.  You can set it to run as part of your build process, run if from code or within Eclipse or even run it at program run time. There are a wealth of options you can configure.  You can read much more about it right here.

So how do you actually use a texture atlas then?  It’s very simple, first copy the generated png and atlas file to your assets.  The following code shows how to use a TextureAtlas:

package com.gamefromscratch.graphicsdemo;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.Timer.Task;

public class GraphicsDemo implements ApplicationListener {
    private SpriteBatch batch;
    private TextureAtlas textureAtlas;
    private Sprite sprite;
    private int currentFrame = 1;
    private String currentAtlasKey = new String("0001");
    
    @Override
    public void create() {        
        batch = new SpriteBatch();
        textureAtlas = new TextureAtlas(Gdx.files.internal("data/spritesheet.atlas"));
        AtlasRegion region = textureAtlas.findRegion("0001");
        sprite = new Sprite(region);
        sprite.setPosition(120, 100);
        sprite.scale(2.5f);
        Timer.schedule(new Task(){
                @Override
                public void run() {
                    currentFrame++;
                    if(currentFrame > 20)
                        currentFrame = 1;
                    
                    // ATTENTION! String.format() doesnt work under GWT for god knows why...
                    currentAtlasKey = String.format("%04d", currentFrame);
                    sprite.setRegion(textureAtlas.findRegion(currentAtlasKey));
                }
            }
            ,0,1/30.0f);
    }

    @Override
    public void dispose() {
        batch.dispose();
        textureAtlas.dispose();
    }

    @Override
    public void render() {        
        Gdx.gl.glClearColor(0, 0, 0, 1);
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        batch.begin();
        sprite.draw(batch);
        batch.end();
    }

    @Override
    public void resize(int width, int height) {
    }

    @Override
    public void pause() {
    }

    @Override
    public void resume() {
    }
}

Here is the HTML5 version of the above code:

As you can see, it’s remarkably consistent.  The majority of the code above is actually part of the demo related, as opposed to being part of using a TextureAtlas.  Instead the only import new code is:

batch = new SpriteBatch();
textureAtlas = new TextureAtlas(Gdx.files.internal("data/spritesheet.atlas"));
AtlasRegion region = textureAtlas.findRegion("0001");
sprite = new Sprite(region);

Just like working with a texture, but instead you load a TextureAtlas.  Then instead of assign the texture to the sprite, you use an AtlasRegion, which describes the coordinates of the individual sprite within the spritesheet.  You get the region by name by calling the findRegion() method and passing the key.  Remember this value is set by the file names of the source images.  The TextureAtlas needs to be dispose()’d or you will leak memory.

As you can see by the call:

sprite.setRegion(textureAtlas.findRegion(currentAtlasKey));

You can change the region within the sprite sheet that the sprite will refer to by calling setRegion().

The rest of the code simply positions and scales the sprite up 2.5x times.  We then schedule a Task using Timer.schedule().  This task will be called ever 30th of a second.  It simply changes the key we will use within the TextureAtlas.   In this case the files were named 0001.png, 0002.png, etc…  so we want a value between 0001 and 0020.  We then use this value to update the region the sprite refers to.  As a result every 30th of a second, the sprite moves on to the next frame of animation, rolling over when it gets to the end.

EDIT: I should state for the record, this is NOT how you would use a TextureAtlas to perform animation, this code was simply for demonstration purposes.  There are dedicated animation classes and we will cover them later on.

Be warned though, if you try to run this under GWT, you will see:

image

This is because the GWT compiler ( this has nothing to do with LibGDX ) doesn’t support String.format() for some reason.  If you want to run this example in a browser you can simply replace

currentAtlasKey = String.format("%04d", currentFrame);

With:

String base = new String();                    if(currentFrame >= 10)              base = "00";          else              base = "000";            currentAtlasKey = base + currentFrame;

Now the HTML target should run just fine.

In the next part we will take a look at controlling input in LibGDX.

Configuring LibGDX to use GL 2

It was brought to my attention by Mario Zechner that LibGDX is not limited to power of 2 texture sizes.  That instead is an artefact of OpenGL ES 1.  If you run using OpenGL ES2 it will work fine.  That said, OpenGL and the underlying hardware still perform better if you stick to power of two.  If you want to use GL2, you set it during as part of the configuration process, we discussed briefly in the Hello World tutorial.  Simply set the useGL20 value to true in the configuration you pass in to your application listener.  Here for example is Main from the desktop project configured to use GL 2.

public class Main {
    public static void main(String[] args) {
        LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
        cfg.title = "graphicsdemo";
        cfg.useGL20 = true;
        cfg.width = 480;
        cfg.height = 320;
        
        new LwjglApplication(new GraphicsDemo(), cfg);
    }
}

Remember of course to configure this for all targets you are supporting.

EDIT:

12/18/2013 – It was pointed out to me that I didn’t include the atlas files, making this tutorial hard to follow along with.  I have included an archive of the data folder used for this example, you can download it here.

Programming Java Tutorial LibGDX


Previous PartTable Of ContentsNext Part
Scroll to Top