2D动画是一种用于使用静态图像创建运动错觉的技术。 本文介绍如何使用LibGDX的动画类来创建动画。
动画由多个帧组成,以设定的间隔以序列显示。 运行 人的动画可以通过循环运行并播放这些图像来实现。
以下“sprite sheet”图像显示一个男人跑步的完整循环。 每个框包含一个动画帧。 当这些帧在一段时间内顺序显示时,它们就成了动画。
帧速率是每秒更改帧的频率。 示例精灵表的完整运行周期有30帧(6列和5行)。 如果该动画在一秒钟内完成一个周期,则每秒必须显示30帧,因此帧速率为30 FPS。 每帧的时间(称为帧时间或间隔时间)是FPS的倒数,在这种情况下为每帧0.033秒。
动画是一个非常简单的状态机。 跑步的人根据精灵画面有30个状态。 编号的框架代表一个正在跑步的人经历的状态,一次只有一个。 当前状态由动画开始以来的时间量决定。 如果少于0.033秒,我们在状态1,所以第一个sprite被绘制。 如果我们在0.033和0.067秒之间,那么我们在状态2,依此类推。 如果动画循环,则在显示所有帧后返回到第一帧。
动画类
LibGDX's Animation (code) 类可用于轻松管理动画。 它的构造函数 具有图像列表和帧间隔时间。 在播放过程中,其getKeyFrame方法会根据 已用时间参数,返回该时间的适当图像。
TextureAtlas使用示例
LibGDX's TextureAtlas (code)通常用于将许多单独的纹理区域组合成的纹理集合,以减少昂贵的绘图调用。 (details here).。
TexturePacker和TextureAtlas提供了一种方便的方法来生成动画。 动画的所有源图像应以最后的下划线和帧号命名,如running_0.png,running_1.png,running_2.png等。TexturePacker将自动使用这些数字作为帧号(只要将参数useIndexes设置为真)。
加载TextureAtlas之后,可以立即获取完整的框架数组,并将其传递到Animation构造函数中:
public Animation<TextureRegion> runningAnimation;
//...
runningAnimation =
new Animation<TextureRegion>(0.033f, atlas.findRegions("running"), PlayMode.LOOP);
精灵示例演示
以下代码片段将使用animation_sheet.png sprite-sheet创建一个动画,并将动画渲染到屏幕。
public class Animator implements ApplicationListener {
// Constant rows and columns of the sprite sheet
private static final int FRAME_COLS = 6, FRAME_ROWS = 5;
// Objects used
Animation<TextureRegion> walkAnimation; // Must declare frame type (TextureRegion)
Texture walkSheet;
SpriteBatch spriteBatch;
// A variable for tracking elapsed time for the animation
float stateTime;
@Override
public void create() {
// Load the sprite sheet as a Texture
walkSheet = new Texture(Gdx.files.internal("animation_sheet.png"));
// Use the split utility method to create a 2D array of TextureRegions. This is
// possible because this sprite sheet contains frames of equal size and they are
// all aligned.
TextureRegion[][] tmp = TextureRegion.split(walkSheet,
walkSheet.getWidth() / FRAME_COLS,
walkSheet.getHeight() / FRAME_ROWS);
// Place the regions into a 1D array in the correct order, starting from the top
// left, going across first. The Animation constructor requires a 1D array.
TextureRegion[] walkFrames = new TextureRegion[FRAME_COLS * FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++) {
for (int j = 0; j < FRAME_COLS; j++) {
walkFrames[index++] = tmp[i][j];
}
}
// Initialize the Animation with the frame interval and array of frames
walkAnimation = new Animation<TextureRegion>(0.025f, walkFrames);
// Instantiate a SpriteBatch for drawing and reset the elapsed animation
// time to 0
spriteBatch = new SpriteBatch();
stateTime = 0f;
}
@Override
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // Clear screen
stateTime += Gdx.graphics.getDeltaTime(); // Accumulate elapsed animation time
// Get current frame of animation for the current stateTime
TextureRegion currentFrame = walkAnimation.getKeyFrame(stateTime, true);
spriteBatch.begin();
spriteBatch.draw(currentFrame, 50, 50); // Draw current frame at (50, 50)
spriteBatch.end();
}
@Override
public void dispose() { // SpriteBatches and Textures must always be disposed
spriteBatch.dispose();
walkSheet.dispose();
}
}
使用以下的构造函数可以快速创建动画
Method signature | Description |
---|---|
Animation (float frameDuration, TextureRegion... keyFrames) | 第一个参数是帧时间,第二个参数是构成动画的区域(帧)数组 |
最佳做法
将帧与其他精灵一起打包成一个纹理,以优化渲染。 这用TexturePacker很容易完成。
根据游戏类型确定合理数量的帧。 对于复古的街机风格,10 fps可能就足够了,而更逼真的运动需要更多的帧。
资源
在这里获取 sprite-sheet 资源 here.