参考:
官方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)
}
}
实际效果

浙公网安备 33010602011771号