android游戏开发框架libgdx的使用(二十三)—使用Universal Tween Engine实现动画效果

转载声明:http://www.cnblogs.com/htynkn/archive/2012/08/28/libgdx_23.html

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;
 
    @Override
    public 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;
        }
    }
 
    @Override
    public 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;
        }
    }
}

然后来写具体的动画和绘制部分。为了方便演示我编写一个随着点击移动的小图标的例子。

我的图标是news。声明image和stage的绘制和原来一样。

先声明一个动画管理器

private TweenManager tweenManager = new TweenManager();

然后将我们的Image注册一下

Tween.registerAccessor(Image.class, new ActorAccessor());

同时实现InputProcessor接口以接收触碰事件。

在touchDown方法中添加

@Override
    public 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;
 
    @Override
    public 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);
    }
 
    @Override
    public void dispose() {
 
    }
 
    @Override
    public 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();
    }
 
    @Override
    public void resize(int width, int height) {
    }
 
    @Override
    public void pause() {
    }
 
    @Override
    public void resume() {
    }
 
    @Override
    public boolean keyDown(int keycode) {
        // TODO Auto-generated method stub
        return false;
    }
 
    @Override
    public boolean keyUp(int keycode) {
        // TODO Auto-generated method stub
        return false;
    }
 
    @Override
    public boolean keyTyped(char character) {
        // TODO Auto-generated method stub
        return false;
    }
 
    @Override
    public 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;
    }
 
    @Override
    public boolean touchUp(int x, int y, int pointer, int button) {
        // TODO Auto-generated method stub
        return false;
    }
 
    @Override
    public boolean touchDragged(int x, int y, int pointer) {
        // TODO Auto-generated method stub
        return false;
    }
 
    @Override
    public boolean touchMoved(int x, int y) {
        // TODO Auto-generated method stub
        return false;
    }
 
    @Override
    public boolean scrolled(int amount) {
        // TODO Auto-generated method stub
        return 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;
 
    @Override
    public 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;
        }
    }
 
    @Override
    public 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);

实现的就是一般移动一般旋转和放大的效果。

效果:

效果是一段视频  通过最上方的转载声明 查看原帖吧

 

posted @ 2013-02-04 21:30  王世桢  阅读(452)  评论(0)    收藏  举报