libgdx的ui庫可以實現一些動畫效果,但是做遊戲來說可能有些不足。Universal Tween Engine是一個純java實現的動畫庫。
地址:http://code.google.com/p/java-universal-tween-engine/
只要能夠用float表示的一切java對象它可以讓它動畫化,可以使用於Libgdx、Android、Swing等等。
Universal Tween Engine使用一般流程
使用Universal Tween Engine最重要的一個步驟就是實現TweenAccessor介面,這個介面定義了getValues和setValues方法。
然後Engine中註冊對應的介面。然後定義一些動畫效果並添加到管理器中。最後用update方法更新時間。
具體的可以參考一下Wiki:http://code.google.com/p/java-universal-tween-engine/wiki/GetStarted
在libgdx中實現簡單動畫
我比較喜歡使用Stage,所以下面的例子都是Stage中的。
首先實現TweenAccessor介面,我沒有區分對待,比如給Image寫一個,再給Button寫個啥的。我直接給Actor寫了一個,這樣都可以用。
getValues和setValues中我定義了3中操作:只修改X值;只修改Y值;修改X和Y值。
public static final int POSITION_X = 1;public static final int POSITION_Y = 2;public static final int POSITION_XY = 3;
這裡注意一下getValues的傳回值,你修改或者操作了幾個值就返回幾。
代碼如下:
package com.cnblogs.htynkn;import aurelienribon.tweenengine.TweenAccessor;import com.badlogic.gdx.scenes.scene2d.Actor;public class ActorAccessor implements TweenAccessor {public static final int POSITION_X = 1;public static final int POSITION_Y = 2;public static final int POSITION_XY = 3;@Overridepublic int getValues(Actor target, int tweenType, float[] returnValues) {switch (tweenType) {case POSITION_X:returnValues[0] = target.x;return 1;case POSITION_Y:returnValues[0] = target.y;return 1;case POSITION_XY:returnValues[0] = target.x;returnValues[1] = target.y;return 2;default:assert false;return -1;}}@Overridepublic void setValues(Actor target, int tweenType, float[] newValues) {switch (tweenType) {case POSITION_X:target.x = newValues[0];break;case POSITION_Y:target.y = newValues[0];break;case POSITION_XY:target.x = newValues[0];target.y = newValues[1];break;default:assert false;break;}}}
然後來寫具體的動畫和繪製部分。為了方便示範我編寫一個隨著點擊移動的小表徵圖的例子。
我的表徵圖是。聲明image和stage的繪製和原來一樣。
先聲明一個動畫管理員
private TweenManager tweenManager = new TweenManager();
然後將我們的Image註冊一下
Tween.registerAccessor(Image.class, new ActorAccessor());
同時實現InputProcessor介面以接收觸碰事件。
在touchDown方法中添加
@Overridepublic boolean touchDown(int x, int y, int pointer, int button) {Vector3 vector3 = new Vector3(x, y, 0);stage.getCamera().unproject(vector3);Tween.to(image, ActorAccessor.POSITION_XY, 1.0f).ease(Bounce.OUT).target(vector3.x, vector3.y).start(tweenManager);return false;}
說明一下,因為Stage的座標和預設的Input的座標不一致,所以通過unproject轉化一下。
Tween.to(image, ActorAccessor.POSITION_XY, 1.0f)代表操作image對象移動。target(vector3.x, vector3.y)代表移動的目標。
ease(Bounce.OUT)聲明了緩衝效果,具體的效果可以參考http://robertpenner.com/easing/easing_demo.html
start(tweenManager)啟動管理器。
在render方法中添加
tweenManager.update(Gdx.graphics.getDeltaTime());
讓管理器的時間更新。
完整代碼:
package com.cnblogs.htynkn;import aurelienribon.tweenengine.Tween;import aurelienribon.tweenengine.TweenManager;import aurelienribon.tweenengine.equations.Bounce;import com.badlogic.gdx.ApplicationListener;import com.badlogic.gdx.Gdx;import com.badlogic.gdx.InputMultiplexer;import com.badlogic.gdx.InputProcessor;import com.badlogic.gdx.graphics.GL10;import com.badlogic.gdx.graphics.g2d.TextureAtlas;import com.badlogic.gdx.math.Vector3;import com.badlogic.gdx.scenes.scene2d.Stage;import com.badlogic.gdx.scenes.scene2d.ui.Image;public class App implements ApplicationListener, InputProcessor {Stage stage;private TweenManager tweenManager = new TweenManager();Image image;@Overridepublic void create() {stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),true);TextureAtlas atlas = new TextureAtlas("packer/test.pack");image = new Image(atlas.findRegion("news"));image.x = 20;image.y = 20;stage.addActor(image);Tween.registerAccessor(Image.class, new ActorAccessor());InputMultiplexer multiplexer = new InputMultiplexer();multiplexer.addProcessor(this);multiplexer.addProcessor(stage);Gdx.input.setInputProcessor(multiplexer);}@Overridepublic void dispose() {}@Overridepublic void render() {tweenManager.update(Gdx.graphics.getDeltaTime());Gdx.gl.glClearColor(1, 1, 1, 1);Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);stage.act(Gdx.graphics.getDeltaTime());stage.draw();}@Overridepublic void resize(int width, int height) {}@Overridepublic void pause() {}@Overridepublic void resume() {}@Overridepublic boolean keyDown(int keycode) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean keyUp(int keycode) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean keyTyped(char character) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean touchDown(int x, int y, int pointer, int button) {Vector3 vector3 = new Vector3(x, y, 0);stage.getCamera().unproject(vector3);Tween.to(image, ActorAccessor.POSITION_XY, 1.0f).ease(Bounce.OUT).target(vector3.x, vector3.y).start(tweenManager);return false;}@Overridepublic boolean touchUp(int x, int y, int pointer, int button) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean touchDragged(int x, int y, int pointer) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean touchMoved(int x, int y) {// TODO Auto-generated method stubreturn false;}@Overridepublic boolean scrolled(int amount) {// TODO Auto-generated method stubreturn false;}}
因為是動畫效果,這裡就不貼出了,文章末尾會有一個小視頻的。
使用TimeLine實現更多動畫效果
上面只是一個簡單的移動效果,但就動畫而言這個顯然是不夠的。如果希望實現一個漸漸顯示的效果怎麼辦?
還是想想TweenAccessor介面,只要float類型的值就行了。所以同樣的我們可以實現修改透明程度、大小等等實現更多的效果。
我最終選用了六種效果:
public static final int POS_XY = 1;public static final int CPOS_XY = 2;public static final int SCALE_XY = 3;public static final int ROTATION = 4;public static final int OPACITY = 5;public static final int COLOR = 6;
實現修改X和Y值,修改X和Y值(包括對象自身大小),修改縮放,修改旋轉,修改透明,修改顏色。
代碼如下:
package com.cnblogs.htynkn;import aurelienribon.tweenengine.TweenAccessor;import com.badlogic.gdx.graphics.Color;import com.badlogic.gdx.scenes.scene2d.Actor;public class ActorAccessor implements TweenAccessor {public static final int POS_XY = 1;public static final int CPOS_XY = 2;public static final int SCALE_XY = 3;public static final int ROTATION = 4;public static final int OPACITY = 5;public static final int TINT = 6;@Overridepublic int getValues(Actor target, int tweenType, float[] returnValues) {switch (tweenType) {case POS_XY:returnValues[0] = target.x;returnValues[1] = target.y;return 2;case CPOS_XY:returnValues[0] = target.x + target.width / 2;returnValues[1] = target.y + target.height / 2;return 2;case SCALE_XY:returnValues[0] = target.scaleX;returnValues[1] = target.scaleY;return 2;case ROTATION:returnValues[0] = target.rotation;return 1;case OPACITY:returnValues[0] = target.color.a;return 1;case TINT:returnValues[0] = target.color.r;returnValues[1] = target.color.g;returnValues[2] = target.color.b;return 3;default:assert false;return -1;}}@Overridepublic void setValues(Actor target, int tweenType, float[] newValues) {switch (tweenType) {case POS_XY:target.x = newValues[0];target.y = newValues[1];break;case CPOS_XY:target.x = newValues[0] - target.width / 2;target.y = newValues[1] - target.height / 2;break;case SCALE_XY:target.scaleX = newValues[0];target.scaleY = newValues[1];break;case ROTATION:target.rotation = newValues[0];break;case OPACITY:Color c = target.color;c.set(c.r, c.g, c.b, newValues[0]);target.color = c;break;case TINT:c = target.color;c.set(newValues[0], newValues[1], newValues[2], c.a);target.color = c;break;default:assert false;}}}
因為Actor中的color是final,所以不能修改,自己改一下原始碼吧。
TimeLine是Universal Tween Engine中的一大利器,可以實現平行和順序動畫。
比如
Timeline.createSequence().beginSequence().push(Tween.to(image, ActorAccessor.POS_XY, 1.0f).target(100,100)).push(Tween.to(image, ActorAccessor.POS_XY, 1.0f).target(200,20)).start(tweenManager);
就表示先移動到100,100處在移動到200,20處。
再比如
Timeline.createParallel().beginParallel().push(Tween.to(image, ActorAccessor.CPOS_XY, 1.0f).target(vector3.x, vector3.y)).push(Tween.to(image, ActorAccessor.ROTATION, 1.0f).target(360)).push(Tween.to(image, ActorAccessor.SCALE_XY, 1.0f).target(1.5f, 1.5f)).end().start(tweenManager);
實現的就是一般移動一般旋轉和放大的效果。
效果: