Friday, July 20, 2018

Basic Sprite Animations in libGDX

In the last post we discussed how to use the texture packer to create sprites. And now we will take a look into how to put these sprites into a libGDX application.

First things first, you need to copy the files you create with the texture packer into the app's asset folder. These assets are typically stored in the Android folder of the app, so in the case of Dot's Dots, the directory is something like this:

/home/netbook/AndroidStudioProjects/dotsdots/android/assets


Don't forget there are at least two files that need to be copied. The PNG file(s), which are the images, and the ATLAS file, which is a map of the images.

Now that the image files are in place, let's look at the code used to animate the sprite.
package com.petenotpete.dotsdots;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureRegion;

public class DotsDots extends ApplicationAdapter {
private SpriteBatch batch;
private TextureAtlas textureAtlas;
private Animation<TextureRegion> animation;
private float elapsedTime = 0;

@Override
public void create () {
batch = new SpriteBatch();
textureAtlas = new TextureAtlas(Gdx.files.internal("dotsprite.atlas"));
animation = new Animation<TextureRegion>(1/60f, textureAtlas.getRegions());
}

@Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

batch.begin();
elapsedTime += Gdx.graphics.getDeltaTime();
batch.draw(animation.getKeyFrame(elapsedTime, true), 0, 0);
batch.end();
}

@Override
public void dispose () {
batch.dispose();
textureAtlas.dispose();
}
}
Now let's look at the code. The magic here is this line:
animation = new Animation(1/60f, textureAtlas.getRegions());
This sets the animation speed (60fps in this case) and the sprite frames from the texture atlas. The next key part are these lines:
elapsedTime += Gdx.graphics.getDeltaTime();
batch.draw(animation.getKeyFrame(elapsedTime, true), 0, 0);
The elapsed time lets the animation know what frame should be displayed, and the 'true' following it shows that the animation should loop. So in this animation, it uses all the sprites on the sprite sheet, but oftentimes we will have more than one image or animation on a sprite sheet and will need to differentiate between them. If you use the libGDX texture packer to create the sprites, the sprite atlas names the frames after each image's individual filename. The best way to use this to your advantage is to add a prefix to each different image in a spritesheet and number them sequentially (like: walk001.png, walk002.png, walk003.png, run001.png, run002.png, run003.png, etc.). In the code you can separate the animations as follows:
public void create() {
batch = new SpriteBatch();
textureAtlas = new TextureAtlas(Gdx.files.internal("dotsprite.atlas"));

TextureRegion[] walkFrames = new TextureRegion[10];

walkFrames[0] = (textureAtlas.findRegion("0001"));
walkFrames[1] = (textureAtlas.findRegion("0002"));
walkFrames[2] = (textureAtlas.findRegion("0003"));
walkFrames[3] = (textureAtlas.findRegion("0004"));
walkFrames[4] = (textureAtlas.findRegion("0005"));
walkFrames[5] = (textureAtlas.findRegion("0006"));
walkFrames[6] = (textureAtlas.findRegion("0007"));
walkFrames[7] = (textureAtlas.findRegion("0008"));
walkFrames[8] = (textureAtlas.findRegion("0009"));
walkFrames[9] = (textureAtlas.findRegion("0010"));

walkAnimation = new Animation(1/60f, walkFrames);

//or you can just pass the frames directly

runAnimation = new Animation(0.1f,
(textureAtlas.findRegion("0011")),
(textureAtlas.findRegion("0012")),
(textureAtlas.findRegion("0013")),
(textureAtlas.findRegion("0014")),
(textureAtlas.findRegion("0015")),
(textureAtlas.findRegion("0016")),
(textureAtlas.findRegion("0017")),
(textureAtlas.findRegion("0018")),
(textureAtlas.findRegion("0019")),
(textureAtlas.findRegion("0020")));
}
And there you go, you have a sprite moving around.