• Cocos spine(加载、换装、挂点、碰撞检测、融合、叠加、cacheMode、合批、置灰、骨骼跟随鼠标旋转)


    参考:

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

    官方文档:spine组件参考

    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)
        }
    } 

    实际效果  

     

  • 相关阅读:
    git 常用命令 command 1.0(本地 local repository 对远程仓库 remote repository 的操作)
    git 常用命令 command
    git .gitignore 忽略列表
    节点的类型
    Error.name 六种值对应的信息
    js或jquery中的验证
    转载:JavaWeb学习总结(五十三)——Web应用中使用JavaMail发送邮件
    转载:JavaWeb学习总结(五十二)——使用JavaMail创建邮件和发送邮件
    转载:JavaWeb学习总结(五十一)——邮件的发送与接收原理
    转载:JavaWeb学习总结(五十)——文件上传和下载
  • 原文地址:https://www.cnblogs.com/gamedaybyday/p/16183658.html
Copyright © 2020-2023  润新知