Cocos Creator Spine 换装
实现的成果
最终实现的效果是: 可以使用单张纹理构建自定义附件(attachment), 实现 Spine 骨骼动画的局部换装.
这么做的原因
Cocos Creator 官方提供了 Spine 动画的一些功能, 如, 替换附件(attachment) 实现局部换装, 节点挂载,等另外的我没有用到,也就没怎么看.
官方提供的 替换附件是你在Spine里面编辑好得附件, 然后导出到CocosCreator编辑器里面,实现换装.
他这个缺点就是你要更换的纹理也在导出的图集里面, 如果你要更换的东西很多, 后面这个图集会越来越大, 后面你可能会为你要换装的部件专门导出一个图集, 但是实际上是没有解决这个问题.
我可以使用单张纹理构建附件, 实现局部换装.
思路
在理解思路的时候需要你知道 骨骼(bone) 插槽(slot) 附件(attachment) 之间的关系.
1.获取任意一个插槽拿到插槽上面装载的附件解析这个数据类型
2.构建这个数据类型(附件)
3.用我们自定义的附件替换 或 设置附件.
解析数据
数据结构
RegionAttachment
-TextureAtlasRegion
-TextureAtlasPage
-sp_SkeletonTexture
这个是上层数据类型使用了下层的数据类型.
我们从最下层开始说明.
我这里使用的是 ts 脚本语言.
// 构建 spine TextureAtlasRegion 需要的数据结构
type TextureAtlasPage = {
name: string;
height: number;
width: number;
texture: sp_SkeletonTexture;
magFilter: number;
minFilter: number;
uWrap: number;
vWrap: number;
}
type _size = {
width: number;
height: number;
}
type sp_SkeletonTexture = {
_image: _size; // 图集的宽高
_texture: cc.Texture2D; // cocos 原始纹理类型
}
// 如果研究过图集里面的参数这一块应该很简单
type TextureAtlasRegion = {
degrees: number; // 图集里面旋转的角度
height: number; // 图片在图集里面的高度
u: number; // 要裁剪的图片在图集中的 uv 坐标, 也就是 图片的四个顶点 在 整个图集中所占用的百分比.
u2: number;
v: number;
v2: number;
width: number; //图片在图集里面的宽度
x: number; // 图片在图集里面的x坐标
y: number;
index: number;
name: string; // 插槽名字
offsetX: number; // 裁剪出来的图片距离渲染尺寸的偏移量
offsetY: number;
originalHeight: number; // 图集的高度
originalWidth: number;
rotate: boolean; // 是否旋转
page: TextureAtlasPage;
renderObject: TextureAtlasRegion;
texture: sp_SkeletonTexture;
}
构建自定义附件
因为我们使用的是单张纹理你可以将图集看成, 就是要替换的纹理.
加载单张纹理
cc.loader.loadRes(`<url>`, cc.Texture2D, (err: any, tex:cc.Texture2D) => {
if(!!err) {
console.error("纹理加载失败");
return;
}
this.setRegion(hand, tex, "1.png", "Arms");
});
构造自定义附件数据,替换它
setRegion(slot: any, tex: cc.Texture2D, texName:string, slotName: string) {
let _s: _size = {
width: tex.width,
height: tex.height,
}
let sps: sp_SkeletonTexture = {
_image: _s,
_texture: tex,
}
let page: TextureAtlasPage = {
name: texName,
height: tex.height,
width: tex.width,
texture: sps,
magFilter: tex["_magFilter"],
minFilter: tex["_minFilter"],
uWrap: tex["_wrapS"],
vWrap: tex["_wrapT"],
}
let region: TextureAtlasRegion = {
degrees: 0,
height: tex.height, // 裁剪的数据 宽高
width: tex.width,
index: -1,
name: slotName,
x: 0,
y: 0,
offsetX: 0,
offsetY: 0,
originalHeight: tex.height,
originalWidth: tex.width,
rotate: false,
page: page,
renderObject: null,
texture: sps,
u: 0,
u2: 1,
v: 0,
v2: 1,
};
region.renderObject = region;
let attachment = new sp["spine"].RegionAttachment(slotName);
attachment.setRegion(region);
// console.log(sp.spine.AtlasAttachmentLoader.newRegionAttachment);
attachment.height = tex.height;
attachment.width = tex.width;
let half_width = tex.width / 2;
let half_height = tex.height / 2;
let offset: Float32Array = new Float32Array([-half_width, -half_height, -half_width, half_height, half_width, half_height, half_width, -half_height]);
attachment.offset = offset;//slot.offset;
attachment.path = slotName;
// attachment.rotation = 20;//slot.rotation;
// attachment.scaleX = 1;//slot.scaleX;
// attachment.scaleY = 1;//slot.scaleY;
// attachment.x = 49.84;//slot.x;
// attachment.y = -5.44;//slot.y;
slot.setAttachment(attachment);
// console.log(slot)
// console.log(attachment);
}
RegionAttachment.offset 这种类型
这个类型如果设置不正确, 就会渲染不出图片, 经过我的分析, 他其实就是以 图片的中心为坐标系构建图片周围四个顶点的坐标.
顺序必须是一致, 左下 -> 左上 -> 右上 -> 右下
设置附件的旋转角度没效果
这里可以去旋转插槽关联的骨骼去解决这个问题.
注意: 这个骨头不要录制动画, 让然你设置完成之后, 已播放动画,就会又回去了.