一個好的游戲總歸需要動畫,Libgdx提供了Animation類來管理控制動畫。
Animation: 儲存了一組TextureRegion,然後順序播放。例如在主角奔跑、跳躍的時候都需要用到Animation。每一個TextureRegion都是一幀,多幀組成了一組動畫。
上面的兩幅圖來自官方的wiki。 每個紅色方框代表這一幀動畫,代表了一個角色的一個跑步過程。我們將會用TextureRegion中split方法來切割這個圖片。
在Animation中有一個參數 frameDuration 這個代表了每一幀圖片在屏幕上顯示的時間。1/frameDuration 就是幀率,也就是一秒鐘顯示多少幅圖片。
在很多示例代碼中都設置了這個stateTime,現在說說我的理解,stateTime就是一個時間計數器,初始化為stateTime=0
/** Returns the current frame number.
* @param stateTime
* @return current frame number */
public int getKeyFrameIndex (float stateTime) {
if (keyFrames.length == 1) return 0;
int frameNumber = (int)(stateTime / frameDuration);
switch (playMode) {
case NORMAL:
frameNumber = Math.min(keyFrames.length - 1, frameNumber);
break;
case LOOP:
frameNumber = frameNumber % keyFrames.length;
break;
case LOOP_PINGPONG:
frameNumber = frameNumber % ((keyFrames.length * 2) - 2);
if (frameNumber >= keyFrames.length) frameNumber = keyFrames.length - 2 - (frameNumber - keyFrames.length);
break;
case LOOP_RANDOM:
int lastFrameNumber = (int) ((lastStateTime) / frameDuration);
if (lastFrameNumber != frameNumber) {
frameNumber = MathUtils.random(keyFrames.length - 1);
} else {
frameNumber = this.lastFrameNumber;
}
break;
case REVERSED:
frameNumber = Math.max(keyFrames.length - frameNumber - 1, 0);
break;
case LOOP_REVERSED:
frameNumber = frameNumber % keyFrames.length;
frameNumber = keyFrames.length - frameNumber - 1;
break;
}
lastFrameNumber = frameNumber;
lastStateTime = stateTime;
return frameNumber;
}
通過源碼可以指定stateTime計算從開始播放動畫開始,我們要計算經過多少個FPS時我們應該顯示下一幀動畫。
private static final String TAG = AnimationTest.class.getSimpleName();
private static final int FRAME_COLS = 6; // 用來做動畫的圖片有6*5
private static final int FRAME_ROWS = 5; // #2
Animation walkAnimation; // 定義Animation對象,用來裝載TextureRegion
Animation reverseWalkAnimation;
Texture walkSheet; // 裝載6*5的圖片
TextureRegion[] walkFrames;
SpriteBatch spriteBatch;
TextureRegion currentFrame; // 記錄關鍵幀,即當前要在batch畫出的TextureRegion
Vector2 postion;
Direction direction;
float stateTime; // 記錄動畫時間
@Override
public void create() {
walkSheet = new Texture(Gdx.files.internal("sprite-animation1.png")); // #9
TextureRegion[][] tmp = TextureRegion.split(walkSheet, walkSheet.getWidth() / FRAME_COLS, walkSheet.getHeight() / FRAME_ROWS); // #10
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];
}
}
walkAnimation = new Animation(0.025f, walkFrames); // #11
spriteBatch = new SpriteBatch(); // #12
postion = new Vector2(50, 50);
direction = Direction.RIGHT;
stateTime = 0f; // #13
Gdx.app.log(TAG, "frameDuration=" + walkAnimation.getFrameDuration());
Gdx.app.log(TAG, "frameDuration=" + walkAnimation.getAnimationDuration());
}
@Override
public void render() {
Gdx.gl.glClearColor(0.39f, 0.58f, 0.92f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);// #14
stateTime += Gdx.graphics.getDeltaTime(); // #15
if (direction == Direction.RIGHT) {
postion.x += 50 * Gdx.graphics.getDeltaTime();
walkAnimation.setPlayMode(PlayMode.LOOP);
} else if (direction == Direction.LEFT) {
postion.x -= 50 * Gdx.graphics.getDeltaTime();
walkAnimation.setPlayMode(PlayMode.REVERSED);
}
if (postion.x + walkFrames[0].getRegionWidth() > Gdx.graphics.getWidth() - 50) {
direction = Direction.LEFT;
} else if (postion.x < 50) {
direction = Direction.RIGHT;
}
currentFrame = walkAnimation.getKeyFrame(stateTime, true); // #16
spriteBatch.begin();
spriteBatch.draw(currentFrame, postion.x, postion.y); // #17
spriteBatch.end();
}
@Override
public void dispose() {
walkSheet.dispose();
}
enum Direction {
LEFT, RIGHT
}
動畫比較難展示,大家自己來運行就可以。下面是log輸出
AnimationTest: frameDuration=0.025
AnimationTest: frameDuration=0.75