Images


Images


June 18, 2018

tutorial libgdx

So far we’ve learned how to draw shapes using libGDX. This is good for simple games and for certain kinds of visualizations. But most “real” games will use images for most of their graphics. This tutorial goes over loading image files, rendering them as textures, and using sprite batching to handle many images at once.

Image Files

Remember that a libGDX project is usually a few different subprojects: a core project that contains the game’s main code, and then platform projects that contain platform-specific wrappers of the core project, which allow you to deploy to multiple platforms. For example you might have an Android project, a web project, and a desktop project in addition to the core project.

LibGDX expects images and other files to be in different places depending on your project setup:

  • If you have an Android project, put your images inside the assets directory inside your Android project.
  • If you do not have an Android project, put your images inside the assets directory inside your core project.

For example, if I’m planning on deploying to Android and web, I’ll have three projects: core, Android, and web. My images will be in the assets directory inside my Android project. Note that the images will be copied automatically into the web project when I deploy it, so I don’t have to copy them manually myself.

Loading Textures

The Texture class represents an image file that’s been loaded into memory to be drawn onto the screen.

To load a texture, you can call the Gdx.files.internal() function to load the image file, and pass that into the Texture constructor:

Texture texture = new Texture(Gdx.files.internal("my-image.png"));

Our texture variable is now ready to be drawn to the screen.

Drawing Textures

To draw a texture to the screen, first we have to create a SpriteBatch object:

SpriteBatch batch = new SpriteBatch();

Then we can call the batch.draw() function to draw our texture:

batch.begin();
batch.draw(texture, 0, 0);
batch.end();

Make sure to include the calls to the batch.begin() and batch.end() functions.

Putting it all together, it looks like this:

package io.happycoding.helloworld;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

public class HelloWorldGame extends ApplicationAdapter {

    SpriteBatch batch;
    Texture texture;

    public void create () {
        batch = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("stanley-1.png"));
    }

    public void render () {
        Gdx.gl.glClearColor(.25f, .25f, .25f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.begin();
        batch.draw(texture, 0, 0);
        batch.end();
    }

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

This program loads stanley-1.png from the assets directory and draws it to the screen. Also note that we dispose of both the SpriteBatch and the Texture when we’re done.

cat image on screen

Image Stretching

The SpriteBatch class contains several versions of the draw() function that take a Texture and other parameters that allow you to draw only part of the texture, or to change the size of the image drawn to the screen. For example, our above example just draws the texture at 0,0 at its default size:

batch.draw(texture, 0, 0);

But we could change that line to something like this:

float x = 125;
float y = 50;
int srcX = 100;
int srcY = 30;
int srcWidth = 300;
int srcHeight = 400;
batch.draw(texture, x, y, srcX, srcY, srcWidth, srcHeight);

This code now draws a section of the image to the screen.

part of cat image on screen

There are a bunch of other useful functions in the SpriteBatch class. Check them out in the libGDX wiki and the libGDX API.

Sprite Batching

We talked about the idea of batching in the graphics tutorial. If you have a bunch of similar shapes, libGDX (more specifically, your graphics card via OpenGL) can use some internal magic logic to draw them faster if you group them together. The same thing is true of images.

For example, consider this program:

package io.happycoding.helloworld;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;

public class HelloWorldGame extends ApplicationAdapter {

    SpriteBatch batch;
    Texture texture;

    public void create () {
        batch = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("stanley-1.png"));
    }

    public void render () {
        Gdx.gl.glClearColor(.125f, .125f, .125f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        for (int i = 0; i < 10000; i++) {
            batch.begin();
            batch.draw(texture, MathUtils.random(Gdx.graphics.getWidth()), MathUtils.random(Gdx.graphics.getHeight()), 50, 50, 0, 1, 1, 0);
            batch.end();
        }

        System.out.println(Gdx.graphics.getFramesPerSecond());
    }

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

This program draws 10000 copies of the same image to the screen. When I run this program on my computer, I get about 20 frames per second.

This code does not take advantage of batching: it calls begin() and end() for every individual image. Compare that to this code:

package io.happycoding.helloworld;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;

public class HelloWorldGame extends ApplicationAdapter {

    SpriteBatch batch;
    Texture texture;

    public void create () {
        batch = new SpriteBatch();
        texture = new Texture(Gdx.files.internal("stanley-1.png"));
    }

    public void render () {
        Gdx.gl.glClearColor(.125f, .125f, .125f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batch.begin();
        for (int i = 0; i < 10000; i++) {
            batch.draw(texture, MathUtils.random(Gdx.graphics.getWidth()), MathUtils.random(Gdx.graphics.getHeight()), 50, 50, 0, 1, 1, 0);
        }
        batch.end();

        System.out.println(Gdx.graphics.getFramesPerSecond());
    }

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

Now our code takes advantage of batching by drawing all of our images between the batch.begin() and batch.end() calls. When I run this program on my computer, I get about 60 frames per second.

10000 cats

There are other ways to optimize this program, such as resizing the image once instead of every time we draw it. But the point is that you can batch draws of the same image to speed your drawing code up. This is useful for stuff like tiling images for a background or as blocks in your game.

Comments

Happy Coding is a community of folks just like you learning about coding.
Do you have a comment or question? Post it here!

Comments are powered by the Happy Coding forum. This page has a corresponding forum post, and replies to that post show up as comments here. Click the button above to go to the forum to post a comment!