参考:

官方example项目 :新建cocos项目时选择example

官方文档:spine组件参考

cocos论坛:【方案】Cocos Creator 的 web/原生多平台 Spine 换装方案解析,附 Demo 源码

cocos版本:2.4.4

spine:3.8.x  (cocos2.3版本之后,才支持spine3.8导出的二进制格式)

 

把spine的一些基本应用合在一个文章里了,免得不好找... = =!

测试素材:包含spine源项目和导出的二进制文件,下载素材

 

一 加载spine

二 挂点

三 骨骼动画的属性设置 enableBatch cacheMode

四 融合动画、叠加动画

五 换装

六 碰撞检测盒子

七 骨骼动画置灰

八 抖动和漩涡效果

九 骨骼跟随鼠标旋转

 

一 加载Spine

1 加载远程spine

直接从官网复制过来的,测试可用。

var spineNode = new cc.Node();
var skeleton = spineNode.addComponent(sp.Skeleton);
this.node.addChild(spineNode);

var image = "http://localhost/download/spineres/1/1.png";
var ske = "http://localhost/download/spineres/1/1.skel";
var atlas = "http://localhost/download/spineres/1/1.atlas";
cc.assetManager.loadAny([{ url: atlas, ext: '.txt' }, { url: ske, ext: '.bin' }], (error, assets) => {
    cc.assetManager.loadRemote(image, (error, texture) => {
        var asset = new sp.SkeletonData();
        asset._nativeAsset = assets[1];
        asset.atlasText = assets[0];
        asset.textures = [texture];
        asset.textureNames = ['1.png'];
        skeleton.skeletonData = asset;
    });
});

  

2 加载项目中spine

        var spineNode = new cc.Node();
        var skeleton = spineNode.addComponent(sp.Skeleton);
        this.node.addChild(spineNode);
        cc.resources.load("spine/raptor-pro", sp.SkeletonData, (error: Error, assets: sp.SkeletonData) => {
            if (error == null) {
                //设置数据
                skeleton.skeletonData = assets;
                //播放默认动画
                skeleton.setAnimation(0, "walk", true);
            }
        });

  

3 spine动画的完成事件和自定义事件监听、替换皮肤

var s: sp.Skeleton;
s.setAnimation(0, "run", false);  //在track0播放动画"run",不循环
s.setCompleteListener((trackEntry, loopCount) => {
       let name = trackEntry.animation.name;  //完成的动画名
});
s.setEventListener((trackIndex, event) => {
        let name = event.data.name;  //触发的事件名
});
s.setSkin("01");  //替换皮肤
s.clearTrack(0);  //停止播放

  

二 挂点

选中一个骨骼动画,在属性面板选择生成挂点。

 

 骨骼动画会生成一堆如下挂点

 

正常的动画如下

 

现在在龙脚上添加一个图,这张图可随着脚摆动。找到脚的节点front-foot3,增加了一个red方块

 

可以看到红色方块是随着脚一起摆动的

 

代码中动态查找挂点,这是官方example中的代码,但是attachUtil这个没有。其实是有的,只是create.d.ts里没有,导致无法使用代码提示。

 

现在使用代码动态生成挂点,并在挂点"front-foot3"上绑定一个蓝色方块。如下图,场景中有一个骨骼动画和一个蓝色方块。

 

代码中生成挂点,并在挂点中添加蓝色方块

@ccclass
export default class Attach extends cc.Component {

    @property(sp.Skeleton)  //恐龙骨骼动画
    skeleton: sp.Skeleton = null;

    @property(cc.Node)      //蓝色方块
    blue: cc.Node = null;

    onLoad() {
        //由于create.d.ts里已经没有attachUtil,所以这里skeleton得转成any。
        let sk: any = this.skeleton;
        let attachUtil = sk.attachUtil;
        //生成挂点
        attachUtil.generateAllAttachedNodes();
        //找到脚的挂点
        let boneNodes = attachUtil.getAttachedNodes("front-foot3");
        //
        let boneNode = boneNodes[0];
        if (boneNode) {
            this.blue.parent = boneNode;
        }
    }
}  

运行效果如图

 

 三 骨骼动画的属性设置

1 Enable Batch

是否开启合批

 

该属性的解释如下

 

 

 

 

 当有80个动画时,开启EnableBatch                                                                       关闭EnableBatch

         

 

cocos官方的建议是:大量简单动画开启,复杂动画关闭。

 

2 cache mode

缓存模式解释如下

 

 

 

80个动画 realtime模式                                                                                       shared_cache模式                                                                                           private_cache模式

            

 

 

                                                                                                                                               

 

 

realtime               普通性能,普通内存占用,支持所有功能,动画融合、动作叠加、自定义事件、换装。 适用于有功能需求的场合。

share_cache       高性能,普通内存占用,不支持动画融合、动作叠加、自定义事件、换装。适用于大量动画且不需要额外功能的场合。

private_cache    高性能,高内存占用不支持动画融合、动作叠加、自定义事件,支持换装。适用于单个或少量动画且需要换装的场合。

 

 四 融合动画、叠加动画

1 融合动画

没有融合的动画。直接从walk瞬间切换到jump动画,没有任何过度,直接从一个动作切到另一个动作。

融合的动画。从walk切换到jump有一个过渡动画,可以看到有一个下蹲的过度效果。

使用setMix设置融合动画

        this.skeleton.setMix("walk", "jump", 0.5);

 

过渡动画原理:

传统的动画,一般是对一个物体对象进行位移、旋转、缩放、变形,然后把关键帧的信息记录下来,在播放的时候按照关键帧时间对物体对象进行位移、

旋转、缩放、变形,并在关键帧与关键帧之间做插值运算。

骨骼动画的特点是,需要做动画的物体对象本身不记录位移、旋转、缩放、变形信息,而是通过了第三方的“骨骼”物体记录动画信息,然后物体对象本身只

记录受到骨骼物体影响的权重。在播放的时候,通过骨骼物体的关键帧和物体对象记录的权重,让动画重现。

 

 

 

 2 叠加动画

行走动画walk

枪动画gun-grab

两个动作叠加起来,可以看到一边行走,一边拔枪

代码如下,将两个动画分别播放在traceIndex0和1上。

        this.skeleton.setAnimation(0, "walk", true);
        this.skeleton.setAnimation(1, "gun-grab", true);  

  当然,你也可以播放3个动画...

        this.skeleton.setAnimation(0, "walk", true);
        this.skeleton.setAnimation(1, "gun-grab", true);
        this.skeleton.setAnimation(2, "roar", true);

  

 五 换装

1 两个骨骼动画之间换装

枪骨骼名"gun"

剑骨骼名 weapon

 

 

 

 现在把龙骑士的枪替换成剑

@ccclass
export default class Batch extends cc.Component {

    @property(sp.Skeleton)  //龙骑士
    raptor: sp.Skeleton = null;

    @property(sp.Skeleton)  //大胡子战士
    hero: sp.Skeleton = null;

    onLoad() {
        let slot1 = this.raptor.findSlot("gun");
        let slot2 = this.hero.findSlot("weapon");
        let attachment = slot2.getAttachment();
        slot1.setAttachment(attachment);
    }
}

   替换效果,可以看到龙骑士的枪变成了剑。(这里龙骑士动画太大,所以整体缩小到了scale=0.3)

 

2 动态任意图片更换

现在resouces下有一把刀,将龙骑士的枪替换成这个刀

 

替换代码如下,sp.SkeletonTexture会报错但是不影响运行

原理就是用动态加载的图片3000.png生成一个TextureAtlasRegion,用这个TextureAtlasRegion来替换龙骑士枪骨骼的TextureAtlasRegion。

@ccclass
export default class Batch extends cc.Component {

    @property(sp.Skeleton)  //龙骑士
    raptor: sp.Skeleton = null;

    onLoad() {
        this.changeSlot(this.raptor, "gun", cc.resources.get("img/3000", cc.Texture2D));
    }

    /**
  * 用外部图片局部换装
  * @param sk   骨骼动画
  * @param slotName  需要替换的插槽名称
  * @param texture   外部图片
  */
    public changeSlot(sk: sp.Skeleton, slotName: string, texture: cc.Texture2D) {
        //获取插槽
        let slot = sk.findSlot(slotName);
        //获取挂件
        let att = slot.attachment;
        //创建region
        let skeletonTexture = new sp.SkeletonTexture();
        skeletonTexture.setRealTexture(texture)
        let page = new sp.spine.TextureAtlasPage()
        page.name = texture.name
        page.uWrap = sp.spine.TextureWrap.ClampToEdge
        page.vWrap = sp.spine.TextureWrap.ClampToEdge
        page.texture = skeletonTexture
        page.texture.setWraps(page.uWrap, page.vWrap)
        page.width = texture.width
        page.height = texture.height

        let region = new sp.spine.TextureAtlasRegion()
        region.page = page
        region.width = texture.width
        region.height = texture.height
        region.originalWidth = texture.width
        region.originalHeight = texture.height

        region.rotate = false
        region.u = 0
        region.v = 0
        region.u2 = 1
        region.v2 = 1
        region.texture = skeletonTexture
        //替换region
        att.region = region
        att.setRegion(region)
        att.updateOffset();
    }
}

 

实现效果,可以看到3000.png这个刀已经被替换上去了

 

六  碰撞检测盒子

 假如动画师在spine骨骼上画了一个BoundingBox,用于伤害判定的范围。

 

 

 在cocos中,从人物骨骼动画中获取这个hurt多边形,根据顶点创建一个PolygonCollider,并绑定到人物上,然后使用碰撞组件PolygonPolygon和Monster的BoxCollider进行碰撞检测

 

//sk是人物的骨骼动画,获取骨骼动画上的挂件
let attachment = this.sk.getAttachment('hero', "hurt")
//获取hero的骨骼
let slot = this.sk.findSlot("hero");
//获取hurt的顶点数组
let arr = {}
let data = attachment.computeWorldVertices(slot, 0, attachment.worldVerticesLength, arr, 0, 2)
console.log("多边形挂件:",attachment);
console.log("多边形顶点:", arr);
//为hero增加多边形Collider
 this.addComponent(cc.PolygonCollider);
let poly:cc.PolygonCollider = this.getComponent(cc.PolygonCollider);
 for(let i=0;i<4;i++){
         poly.points[i].x = arr[i*2];
         poly.points[i].y = arr[i*2+1];
}
//hurt多边形碰撞体,和怪物mosnter的boxCollider进行碰撞检测(Monter.boxCollider为了测试方便保存的static变量)
console.log("碰撞:", cc.Intersection.polygonPolygon((poly as any).world.points, (Monster.boxCollider as any).world.points));

  

 

 

 

七 骨骼动画置灰

 骨骼动画置灰所需要的mtl和effect文件在cocos example的项目里可以找到

 

置灰后的动画

 

 

八 抖动和漩涡效果

 官方的examle里还提供了抖动和漩涡效果

 抖动效果

@ccclass
export default class Batch extends cc.Component {

    @property(sp.Skeleton)  //龙骑士
    raptor: sp.Skeleton = null;

    onLoad() {
        let effect = new sp.VertexEffectDelegate();
        effect.initJitter(20, 20);
        this.raptor.setVertexEffectDelegate(effect);
    }
}

 

漩涡效果

@ccclass
export default class Batch extends cc.Component {

    @property(sp.Skeleton)  //龙骑士
    raptor: sp.Skeleton = null;

    private _swirlTime: number;
    private _bound: cc.Size;
    private _swirlEffect: sp.VertexEffectDelegate;

    onLoad() {
        this._swirlEffect = new sp.VertexEffectDelegate();
        this._swirlEffect.initSwirlWithPowOut(0, 2);
        this.raptor.setVertexEffectDelegate(this._swirlEffect);

        this._swirlTime = 0;
        this._bound = cc.size(this.raptor.node.width, this.raptor.node.height);
    }

    update(dt) {
        this._swirlTime += dt;
        let percent = this._swirlTime % 2;
        if (percent > 1) percent = 1 - (percent - 1);

        let bound = this._bound;
        let swirlEffect = this._swirlEffect.getSwirlVertexEffect();
        swirlEffect.angle = 360 * percent;
        swirlEffect.centerX = bound.width * 0.5;
        swirlEffect.centerY = bound.height * 0.5;

        swirlEffect.radius = percent * Math.sqrt(bound.width * bound.width + bound.height * bound.height);
    }
}

 

九 骨骼跟随鼠标旋转

让龙骑士的头跟随鼠标旋转,主要是获取鼠标和龙骑士的角度,然后设置头部骨骼的bone.data.rotation角度。

@ccclass
export default class Batch extends cc.Component {

    @property(sp.Skeleton)  //龙骑士
    raptor: sp.Skeleton = null;

    onLoad() {
        //鼠标移动事件
        this.node.on(cc.Node.EventType.TOUCH_MOVE, (e: cc.Event.EventTouch) => {
            //将触摸位置转成本地位置
            let pos = e.getLocation();
            pos = this.node.convertToNodeSpaceAR(pos);
            //获取触摸位置和龙骑士的角度
            let angle = Math.atan2(pos.y - this.raptor.node.y, pos.x - this.raptor.node.x);
            angle = angle * 180 / Math.PI;
            //获取头的骨骼,并将角度设置为和鼠标触摸的角度
            let bone: sp.spine.Bone = this.raptor.findBone("head");
            bone.data.rotation = angle;
        }, this)
    }
} 

实际效果  

 

 

posted on 2022-04-23 21:28  gamedaybyday  阅读(12183)  评论(1编辑  收藏  举报