Libgdx播放Spine动画(2)-功能

来源:互联网 发布:失去才知珍惜的小故事 编辑:程序博客网 时间:2024/05/29 10:20


博客已迁移:请前往 AyoCrazy.com—Libgdx播放Spine动画(2) 查看完整版



在上一篇博客《Libgdx播放Spine动画(1)-基础》中,写了关于Libgdx播放Spine动画的基本方法,也作了一个简单的示例。但在实际项目中,仅仅会这些还是远远不够,我们必须掌握Spine的一些高级功能,这样才能充分发挥Spine的特长,为我们的项目增添色彩。Spine的高级特性很多,但大多都是提供给动画设计师用的,程序这边只负责调用就行。这篇主要针对程序可以控制的内容,例如混合动画、无缝切换、事件监听、换肤换装、控制骨骼等。

混合动画

先来了解一下什么是混合动画。比如一个角色有两个动画——走路和射击,如果现在需求是让角色能够边走边射击,一般的做法是再增加一个走路和射击同时进行的动画,但Spine给我们提供了一个混合动画的功能,可以同时播放这两个动画,这大大地提高了开发效率。

  • 第一步,准备资源。我们使用官方例子中的spineboy。如图:
    spineboy
  • 第二步,参照上一篇代码,先让动画跑起来。
    @Override    public void create() {        render = new SkeletonMeshRenderer();        // 获取纹理集合        TextureAtlas tAtlas = new TextureAtlas(Gdx.files.internal("spineboy.atlas"));        // 读取json信息        SkeletonJson sJson = new SkeletonJson(tAtlas);        sJson.setScale(0.5f);// 缩放,以后不可更改        sData = sJson.readSkeletonData(Gdx.files.internal("spineboy.json"));        // 初始化动画信息        AnimationStateData animData = new AnimationStateData(sData);        state = new AnimationState(animData);        // 初始化骨骼信息        skeleton = new Skeleton(sData);        // 初始化batch        polygonBatch = new PolygonSpriteBatch();        // 设置位置        skeleton.setPosition(500, 200);        // 播放动画        state.setAnimation(0, "run", true);    }    这里我们播放了一个"run"的动画。
  • 第三步,增加一个”shoot”的动画。
    state.addAnimation(1, "shoot", true, 0);

运行就能看到,角色跑和射击两个动画同时播放。
特别注意,在state.setAnimation()state.addAnimation() 两个方法中,第一个参数trackIndex很重要,spine在播放混合动画的时候,是按照tranckIndex从小到大的顺序进行的 ,也就是trackIndex值大的动画会覆盖掉值小的动画(只覆盖值大动画所控制的那部分),如果我们把上面例子中两个动画的tranckIndex颠倒过来,效果是完全不同的,读者不妨试一试。

无缝切换

在游戏开发中,动画切换是很常见的。例如一个角色的某个动画是将手举起再放下来,另外一个动画是用手摸摸脸。当播放第一个动画进行到中间的时候,手是处于举起来的状态,这时如果让它播放第二个动画,我们会看到手瞬间就放下来了,然后再去摸脸。这会给人一种卡顿的感觉,给用户带来不好的游戏体验。如果使用混合动画,只需要设置混合时间,在播放第二个动画的时候,会在混合时间内将第一个动画的状态逐渐恢复到第二个动画的初始状态,也就是手是慢慢放下来再去摸脸的(虽然也很快,但不是瞬间放下来的),所以会使游戏更加流畅。
还是来看具体的代码实现:

  • 还是上面的例子,前两步相同,先让人物跑起来。
  • 第二步,我们增加一个按键监听,按J键让人物跳起来,按R键让人物跑起来。代码如下:
    InputAdapter keyListener = new InputAdapter() {            @Override            public boolean keyDown(int keycode) {                if (keycode == Keys.J) {                    state.setAnimation(0, "jump", true);                } else if (keycode == Keys.R) {                    state.setAnimation(0, "run", true);                }                return true;            }        };    Gdx.input.setInputProcessor(keyListener);

运行一下,先做个测试,按J当人物跳到空中的时候,迅速按R让人物接跑的动作。我们发现人物是从空中一瞬间就掉到地上跑起来的,看起来像瞬移。那么我们来解决这个问题。

  • 第三步,我们来设置一个混合时间(mixTime),代码:
    // 设置混合时间    animData.setMix("jump", "run", 0.3f);

这句代码的作用是当我们从jump动画切换到run动画的时候,有0.3秒时间过渡,会将jump的当前状态过渡到run动画的初始状态。再来运行的时候,就会发现人物不是从空中瞬移到地上,而是降落下来的。spine还提供了一个方法,可以设置默认的混合时间,在没有单独设置混合时间的时候,动画切换将会使用默认时间来过渡,可以用

    //设置默认混合时间    animData.setDefaultMix(0.3f);

事件监听

在游戏项目中,对于流程控制肯定少不了事件监听,Spine提供了AnimationStateListener类来监听动画。

        state.addListener(new AnimationStateListener() {            //动画开始时触发            @Override            public void start(int trackIndex) {            }            //event事件是在json文件中定义的            @Override            public void event(int trackIndex, Event event) {            }            //动画被切换时触发            @Override            public void end(int trackIndex) {            }            //动画播放完成时被触发            @Override            public void complete(int trackIndex, int loopCount) {            }        });

直接调用即可,此处不作示例。event事件主要作用是用来播放音效,例如人物走路的时候,希望加一个脚步声,那么可以在脚着地的时刻添加一个event,当监听到这个event的时候,播放一个脚步声音效即可。

骨骼控制

就是通过代码去控制关节运动,这里直接进入示例。需求是让spineboy的枪指向跟随鼠标。

  • 第一步,让角色播放一个待机动画:
        // 播放idle动画        state.setAnimation(0, "idle", true);
  • 第二步,新增一个方法:
    private void update() {        // 获取持枪手臂的骨骼        Bone arm = skeleton.findBone("rear_upper_arm");        // 获取鼠标坐标        Vector2 mouse = new Vector2(Gdx.input.getX(), Gdx.graphics.getHeight() - Gdx.input.getY());        // 获取手臂在屏幕上的坐标        Vector2 armPos = new Vector2(arm.getWorldX() + skeleton.getX(), arm.getWorldY() + skeleton.getY());        // 计算手臂、鼠标连线与X轴的夹角        float angle = mouse.sub(armPos).angle();        // 计算手臂相对于父关节的夹角        float relativeAngle = angle - arm.getParent().getWorldRotationX();        // 设置手臂的夹角,也是相对于父关节        arm.setRotation(relativeAngle);    }

这里我们只是简单将手臂角度对准鼠标,而没有改变枪的角度,若要做得逼真,还需要改变头部、小臂、枪的角度,这里只作演示,具体如何操作可查看官方示例。

  • 第三步,在render() 方法中执行 update() 方法:
    @Override    public void render() {        // 清屏        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);        // 动画控制器更新时间步        state.update(Gdx.graphics.getDeltaTime());        // 动画控制器控制骨骼动画        state.apply(skeleton);        // 在这里进行骨骼控制,顺序不可更改,切记!        update();        // 骨骼逐级进行矩阵变换        skeleton.updateWorldTransform();        // 绘制        polygonBatch.begin();        render.draw(polygonBatch, skeleton);        polygonBatch.end();    }

注意 update() 方法的位置。

换肤换装

Spine中有一个Skin功能,就是皮肤的意思。在上一章讲到skeleton骨骼只包含骨骼信息,并未包含贴图等附件的信息,附件是在skin中定义的。这样做的好处是可以只用一套骨骼,通过切换skin来达到换肤的效果。代码很简单:

    //设置皮肤    skeleton.setSkin("skinName");

其中skinName是必须在json文件中定义好的skin。
我们经常看到很多网游中不同等级的怪物外形是相同的,只是纹理不同,就是这种与换肤类似的方式实现的。

但如果我们想要实现换装功能该怎么办呢?比如,给角色更换武器、盔甲、鞋子、帽子等,我们不可能为每一种可能的组合都在json文件中定义好,只能通过代码动态地生成皮肤。这里将给一个示例:

  • 第一步,使用上一篇博客的资源,先让动画跑起来。
    这里写图片描述
  • 第二步,我们先来试试简单的换肤。包括第一步的代码如下:
 @Override    public void create() {        render = new SkeletonMeshRenderer();        // 获取纹理集合        TextureAtlas tAtlas = new TextureAtlas(Gdx.files.internal("goblins.atlas"));        // 读取json信息        SkeletonJson sJson = new SkeletonJson(tAtlas);        sJson.setScale(1f);// 缩放,以后不可更改        sData = sJson.readSkeletonData(Gdx.files.internal("goblins.json"));        // 初始化动画信息        AnimationStateData animData = new AnimationStateData(sData);        state = new AnimationState(animData);        // 初始化骨骼信息        skeleton = new Skeleton(sData);        // 初始化batch        polygonBatch = new PolygonSpriteBatch();         // 设置位置        skeleton.setPosition(500, 200);        // 播放动画        state.setAnimation(0, "walk", true);        //设置皮肤        skeleton.setSkin("goblingirl");    }

运行效果如图:
这里写图片描述
- 第三步,要进行换装了。现在需求是把女角色的身体换成男身,那么只需要将身体贴图换成男身贴图就行,来看一下代码:

        // 在json文件中找到身体的插槽名,和贴图名        String slotName = "torso";        String attachmentName = "torso";        // 拿到男角色的皮肤skin        Skin goblin = sData.findSkin("goblin");        // 拿到男角色的身体贴图        Attachment att = goblin.getAttachment(skeleton.findSlotIndex(slotName), attachmentName);        // 为身体插槽设置贴图        skeleton.findSlot(slotName).setAttachment(att);        // 在当前皮肤中添加贴图        skeleton.getSkin().addAttachment(skeleton.findSlotIndex(slotName), attachmentName, att);

运行效果:
换身
代码都不难理解。这里说一下最后一行,在这个示例中,不加这一行代码,效果是一样的,但是如果在动画中定义了attachment的关键帧,没有这一行是不行的,因为在动画播放的时候,如果遇到attachment的关键帧,spine是会通过skin去寻找对应的attachment,而不是你设置在skeleton上的attachment,所以这里必须将贴图添加到skin中。读者不妨将上面的代码改动两句:

        // 在json文件中找到身体的插槽名,和贴图名        String slotName = "eyes";        String attachmentName = "eyes closed";

博客已迁移:请前往 AyoCrazy.com—Libgdx播放Spine动画(2) 查看完整版

1 0
原创粉丝点击