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



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




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

混合动画

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

  • 第一步,准备资源。我们使用官方例子中的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) 查看完整版

时间: 2024-09-06 00:42:09

Libgdx播放Spine动画(2)-功能的相关文章

Libgdx播放Spine动画(1)-基础

博客已迁移:更多技术文章请前往AyoCrazy.com-Libgdx播放Spine动画(1) Spine作为2D骨骼动画编辑器中的佼佼者,其高效简洁的操作流程,强大并且实用的各种功能,受到越来越多的动画设计师的青睐.Spine的作者本人也是libgdx的联合作者,其中也大量的用到了libgdx的相关内容,所以spine和libgdx也算是近亲关系.本文就来讲一讲在libgdx中如何播放Spine动画,以及在使用spine动画过程中遇到的各种坑. 原理 在写具体的代码之前,我想把spine动画文件

Android高级控件(二)——SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现

Android高级控件(二)--SurfaceView实现GIF动画架包,播放GIF动画,自己实现功能的初体现 写这个的原因呢,也是因为项目中用到了gif动画,虽然网上有很多的架包可以实现,不过我们还是要追究一下原理怎么做的,我们新建一个GifLibrary,然后右键Properties--Android,我们把架包勾上 然后我们新建一个类GifSurfaceView继承自SurfaceView并且实现它的Callback接口 GifSurfaceView package com.lgl.gif

opengl-按键控制模型播放特定动画(OpenGL)

问题描述 按键控制模型播放特定动画(OpenGL) 导入了一个MD2模型文件,想实现按特定的键播放特定的动画的功能,比如,按"A"播放动画1,按"S"播放动画2.要怎么写代码?

Android实现可播放GIF动画的ImageView_Android

Android的原生控件并不支持播放GIF格式的图片.我们都知道,在Android中如果想要显示一张图片,可以借助ImageView来完成,但是如果将一张GIF图片设置到ImageView里,它只会显示这张图片的第一帧,不会产生任何的动画效果.今天我们来编写一个自定义的增强型ImageView(继承ImageView),可以播放GIF格式的图片,暂且叫做GifImageView吧. 1.自定义属性 <?xml version="1.0" encoding="utf-8&

Android自定义View播放Gif动画的示例

前言 GIF是一种很常见的动态图片格式,在Android中它的使用场景非常多,大到启动页动画.小到一个Loading展示,都可以用GIF动画来完成,使用也很方便,直接从美工那边拿过来用就成.如果项目赶时间或者自定义原生动画太麻烦,GIF都是一个很好的选择,相比于最新的WEBP格式的动画,也有更好的兼容性(毕竟已经出现很多年了). 关于图片加载我一直用的是Google推荐的 Glide ,图片加载和缓存都做的很好,同样也支持GIF动画.不过Glide默认就是循环播放Gif,没有开放相关的接口来控制

PhotoShop制作图片循环滚动播放GIF动画教程

介绍PhotoShop制作屏幕上的图片循环滚动播放GIF动画效果,需要制作的图片要同样尺寸,不同尺寸的要调成同样大小,具体制作方法看教程, 喜欢的同学可以一起来学习一下! 最终效果图: 分类: PS入门教程

PS制作显示器播放效果动画图片

教程教飞特的朋友们用PS制作显示器播放效果动画图片,教程制作难度中等偏上,主要还是介绍PS制作动画的具体流程,这里介绍的是帧动画,对PS8.0以上的版本都适用,好了,先来看看最终的效果图吧: 效果图:   具体的制作过程如下: 效果图:   分类: PS入门教程

Android播放gif动画,增加屏幕掉金币效果

前言:播放gif的版本有很多,我这边使用Android自带的Movie类播放gif动画,也是在别人的基础上进行修改.有同样需求的朋友可以参考我的demo. 1.效果图如下:    2.部分主要代码 MainActivity.java  给封装的GifView设置背景gif图片资源, 绘制金币,同时开启金币屏幕掉下来的效果,监听gif播放完毕动画,结束掉金币的动画   public class MainActivity extends Activity implements OnClickList

按钮-jsp系统,实现播放器限时观看功能

问题描述 jsp系统,实现播放器限时观看功能 要求:用一个网页播放器播放视频.假如一个视频有2个小时,我可以设定其观看4个小时,在这4个小时中他可以随意观看,如果他不想看了可以点暂停按钮,然后这个视频就被暂停了,但是他观看的时间要被记录下来,假如他看了3个小时,他下次观看只剩下1个小时了,等这个小时看完后即不能再观看此视频 解决方案 用ajax上传它的播放和暂停的动作,服务器将这些动作和发生的时间累加起来存在数据库.到了时间不再允许它访问.