对于Spine局部换装图集的支持,论坛里面的帖子大多都是关于Creator2.x的。方案上也是基于Creator2.x。Creator3.x Spine的局部换装只暴露了一个接口就是setSlotTexture
。无法直接使用论坛里面基于Creator2.x的方案。
我目前使用的方案是基于Creator2.x的方案修改而来,需要修改到原生引擎代码和Spine-Wasm的代码。
Creator2.x的方案:spine换装实战总结(基于2.4.7)| 社区征文
最终实现的局部换装代码示例
let texture = spriteFrame.texture as Texture2D;
let rect = spriteFrame.rect;
let originSize = spriteFrame.originalSize;
let originOffset = spriteFrame.offset;
let offset = new Vec2((originSize.width - rect.width) * 0.5 + originOffset.x, (originSize.height - rect.height) * 0.5 + originOffset.y);
let degrees = spriteFrame.rotated ? 270 : 0;
let texSize = new Size(texture.width, texture.height);
skeleton.setSlotTexture2(slotName, texture, texSize, rect, originSize, offset, degrees, true);
一、修改原生引擎换装实现。
我这里使用的是新增一个接口,新增了使用图集换装的接口
void setSlotTexture2(const std::string &slotName, cc::Texture2D *tex2d, cc::Size texSize, cc::Rect rect, cc::Size origSize, cc::Vec2 offset, uint32_t degrees, bool createAttachment);
核心代码和Creator2.x的方案
基本一致
void SkeletonRenderer::setSlotTexture2(const std::string &slotName, cc::Texture2D *tex2d, cc::Size texSize, cc::Rect rect, cc::Size origSize, cc::Vec2 offset, uint32_t degrees, bool createAttachment) {
if (!_skeleton) return;
auto slot = _skeleton->findSlot(slotName.c_str());
if (!slot) return;
auto attachment = slot->getAttachment();
if (!attachment) return;
float u, v, u2, v2;
float width = rect.width;
float height = rect.height;
float texW = texSize.width;
float texH = texSize.height;
if (degrees != 0) {
u = rect.x / texW;
v = rect.y / texH;
u2 = (rect.x + height) / texW;
v2 = (rect.y + width) / texH;
} else {
u = rect.x / texW;
v = rect.y / texH;
u2 = (rect.x + width) / texW;
v2 = (rect.y + height) / texH;
}
if (createAttachment) {
attachment = attachment->copy();
slot->setAttachment(attachment);
_skeleton->recordAttachment(slotName.c_str(), attachment);
}
AttachmentVertices *attachmentVertices = nullptr;
if (attachment->getRTTI().isExactly(spine::RegionAttachment::rtti)) {
auto region = static_cast<RegionAttachment *>(attachment);
region->setRegionWidth(width);
region->setRegionHeight(height);
region->setRegionOriginalWidth(origSize.width);
region->setRegionOriginalHeight(origSize.height);
// region->setWidth(width);
// region->setHeight(height);
region->setRegionOffsetX(offset.x);
region->setRegionOffsetY(offset.y);
region->setUVs(u, v, u2, v2, degrees != 0);
region->updateOffset();
attachmentVertices = static_cast<AttachmentVertices *>(region->getRendererObject());
if (createAttachment) {
attachmentVertices = attachmentVertices->copy();
region->setRendererObject(attachmentVertices);
}
V3F_T2F_C4B *vertices = attachmentVertices->_triangles->verts;
const auto &UVs = region->getUVs();
for (int i = 0, ii = 0; i < 4; ++i, ii += 2) {
vertices[i].texCoord.u = UVs[ii];
vertices[i].texCoord.v = UVs[ii + 1];
}
} else if (attachment->getRTTI().isExactly(spine::MeshAttachment::rtti)) {
auto mesh = static_cast<MeshAttachment *>(attachment);
mesh->setRegionWidth(width);
mesh->setRegionHeight(height);
mesh->setRegionOriginalWidth(origSize.width);
mesh->setRegionOriginalHeight(origSize.height);
mesh->setRegionOffsetX(offset.x);
mesh->setRegionOffsetY(offset.y);
// mesh->setWidth(width);
// mesh->setHeight(height);
mesh->setRegionU(u);
mesh->setRegionV(v);
mesh->setRegionU2(u2);
mesh->setRegionV2(v2);
mesh->setRegionRotate(degrees != 0);
mesh->setRegionDegrees(degrees);
mesh->updateUVs();
attachmentVertices = static_cast<AttachmentVertices *>(mesh->getRendererObject());
if (createAttachment) {
attachmentVertices = attachmentVertices->copy();
mesh->setRendererObject(attachmentVertices);
}
V3F_T2F_C4B *vertices = attachmentVertices->_triangles->verts;
const auto &UVs = mesh->getUVs();
for (size_t i = 0, ii = 0, nn = mesh->getWorldVerticesLength(); ii < nn; ++i, ii += 2) {
vertices[i].texCoord.u = UVs[ii];
vertices[i].texCoord.v = UVs[ii + 1];
}
}
// if (!attachmentVertices) return;
middleware::Texture2D *middlewareTexture = nullptr;
for (auto &it : _slotTextureSet) {
if (it->getRealTexture() == tex2d) {
middlewareTexture = it;
break;
}
}
if (!middlewareTexture) {
middlewareTexture = new middleware::Texture2D();
middlewareTexture->addRef();
middlewareTexture->setRealTexture(tex2d);
}
if (attachmentVertices->_texture) {
attachmentVertices->_texture->release();
}
attachmentVertices->_texture = middlewareTexture;
}
根据Creator2.x
还需修改 MeshAttachment.cpp
中一处关于旋转的修复
// MeshAttachment.cpp中的updateUVs方法
case 270: {
float textureWidth = _regionHeight / (_regionU2 - _regionU);
float textureHeight = _regionWidth / (_regionV2 - _regionV);
u -= _regionOffsetY / textureWidth;
v -= _regionOffsetX / textureHeight;
width = _regionOriginalHeight / textureWidth;
height = _regionOriginalWidth / textureHeight;
for (i = 0; i < n; i += 2) {
_uvs[i] = u + (1 - _regionUVs[i + 1]) * width;
_uvs[i + 1] = v + _regionUVs[i] * height;
}
return;
}
二、修改js/TS增加新的换装接口。
- 修改
platforms/native/engine/jsb-spine-skeleton.js
// 增加setSlotTexture2方法
skeleton.setSlotTexture2 = function (slotName, tex2d, texSize, rect, originSize, offset, degrees, createNew) {
if (this.isAnimationCached()) {
console.error(`Cached mode can't change texture of slot`);
return;
}
if (!this._nativeSkeleton) return;
const slot = this.findSlot(slotName);
if (!slot) {
console.error(`No slot named:${slotName}`);
return;
}
const createNewAttachment = createNew || false;
this._nativeSkeleton.setSlotTexture2(slotName, tex2d, texSize, rect, originSize, offset, degrees, createNewAttachment);
};
- 修改
cc.d.ts
setSlotTexture2(slotName: string, tex2d: Texture2D, texSize: math.Size, rect: math.Rect, originSize: math.Size, offset: math.Vec2, degrees: number, createNew: boolean): void;
二、修改Spine的WASM实现支持图集换装
- 修改
native/cocos/editor-support/spine-wasm/spine-skeleton-instance
新增以下方法
void resizeSlotRegion2(const spine::String &slotName, float width, float height, float rectWidth, float rectHeight, float rectX, float rectY, float originWidth, float originHeight, float offsetX, float offsetY, uint32_t degrees, bool createNew = false);
具体实现代码
void SpineSkeletonInstance::resizeSlotRegion(const spine::String &slotName, uint32_t width, uint32_t height, bool createNew) {
if (!_skeleton) return;
auto* slot = _skeleton->findSlot(slotName);
if (!slot) return;
auto*attachment = slot->getAttachment();
if (!attachment) return;
if (createNew) {
attachment = attachment->copy();
slot->setAttachment(attachment);
}
if (attachment->getRTTI().isExactly(spine::RegionAttachment::rtti)) {
auto *region = static_cast<RegionAttachment *>(attachment);
region->setRegionWidth(width);
region->setRegionHeight(height);
region->setRegionOriginalWidth(width);
region->setRegionOriginalHeight(height);
region->setWidth(width);
region->setHeight(height);
region->setUVs(0, 0, 1.0f, 1.0f, false);
region->updateOffset();
auto *attachmentVertices = static_cast<AttachmentVertices *>(region->getRendererObject());
if (createNew) {
attachmentVertices = attachmentVertices->copy();
region->setRendererObject(attachmentVertices);
}
V3F_T2F_C4B *vertices = attachmentVertices->_triangles->verts;
const auto &UVs = region->getUVs();
for (int i = 0, ii = 0; i < 4; ++i, ii += 2) {
vertices[i].texCoord.u = UVs[ii];
vertices[i].texCoord.v = UVs[ii + 1];
}
} else if (attachment->getRTTI().isExactly(spine::MeshAttachment::rtti)) {
auto *mesh = static_cast<MeshAttachment *>(attachment);
mesh->setRegionWidth(width);
mesh->setRegionHeight(height);
mesh->setRegionOriginalWidth(width);
mesh->setRegionOriginalHeight(height);
mesh->setWidth(width);
mesh->setHeight(height);
mesh->setRegionU(0);
mesh->setRegionV(0);
mesh->setRegionU2(1.0f);
mesh->setRegionV2(1.0f);
mesh->setRegionRotate(true);
mesh->setRegionDegrees(0);
mesh->updateUVs();
auto *attachmentVertices = static_cast<AttachmentVertices *>(mesh->getRendererObject());
if (createNew) {
attachmentVertices = attachmentVertices->copy();
mesh->setRendererObject(attachmentVertices);
}
V3F_T2F_C4B *vertices = attachmentVertices->_triangles->verts;
const auto &UVs = mesh->getUVs();
for (size_t i = 0, ii = 0, nn = mesh->getWorldVerticesLength(); ii < nn; ++i, ii += 2) {
vertices[i].texCoord.u = UVs[ii];
vertices[i].texCoord.v = UVs[ii + 1];
}
}
}
- 修改
native/cocos/editor-support/spine-wasm/spine-type-export
增加导出
.function("resizeSlotRegion", &SpineSkeletonInstance::resizeSlotRegion)
.function("resizeSlotRegion2", &SpineSkeletonInstance::resizeSlotRegion2)
- 重新编译
spine-wasm
这个网上有相关帖子,论坛里面也有,不行也可以问AI
三、修改Spine的TS层支持图集换装
- 修改
cocos/spine/skeleton.ts
public setSlotTexture2(slotName: string, tex2d: Texture2D, texSize: Size, rect: Rect, originSize: Size, offset:Vec2, degrees: number, createNew: boolean): void {
if (this.isAnimationCached()) {
error(`Cached mode can't change texture of slot`);
return;
}
const slot = this.findSlot(slotName);
if (!slot) {
error(`No slot named:${slotName}`);
return;
}
const createNewAttachment = createNew || false;
this._instance!.resizeSlotRegion2(slotName, texSize.width, texSize.height, rect.width, rect.height, rect.x, rect.y, originSize.width, originSize.height, offset.x, offset.y, degrees, createNewAttachment);
if (!this._slotTextures) this._slotTextures = new Map<number, Texture2D>();
let textureID = 0;
this._slotTextures.forEach((value, key) => {
if (value === tex2d) textureID = key;
});
if (textureID === 0) {
textureID = ++_slotTextureID;
this._slotTextures.set(textureID, tex2d);
}
this._instance!.setSlotTexture(slotName, textureID);
}
- 修改
cocos/spine/lib/skeleton-core.d.ts
resizeSlotRegion(slotName: string, width: number, height: number, createNew: boolean);
resizeSlotRegion2(slotName: string, texWidth: number, texHeight: number, rectWidth: number, rectHeight: number, rectX: number, rectY: number, originWidth: number, originHeight: number, offsetX: number, offsetY: number, degrees: number, createNew: boolean);
四、修改引擎适配js代码,增加图集换装支持
- 修改
bin/adapter/engine-adapter.js
skeleton.setSlotTexture2 = function (slotName, tex2d, texSize, rect, originSize, offset, degrees, createNew) {
if (this.isAnimationCached()) {
console.error(`Cached mode can't change texture of slot`);
return;
}
if (!this._nativeSkeleton) return;
const slot = this.findSlot(slotName);
if (!slot) {
console.error(`No slot named:${slotName}`);
return;
}
const createNewAttachment = createNew || false;
this._nativeSkeleton.setSlotTexture2(slotName, tex2d, texSize, rect, originSize, offset, degrees, createNewAttachment);
};
- 修改
bin/adapter/engine-adapter.min.js
skeleton.setSlotTexture2 = function (slotName, tex2d, texSize, rect, originSize, offset, degrees, createNew) {
if (this.isAnimationCached()) {
console.error(`Cached mode can't change texture of slot`);
return;
}
if (!this._nativeSkeleton) return;
const slot = this.findSlot(slotName);
if (!slot) {
console.error(`No slot named:${slotName}`);
return;
}
const createNewAttachment = createNew || false;
this._nativeSkeleton.setSlotTexture2(slotName, tex2d, texSize, rect, originSize, offset, degrees, createNewAttachment);
};
五、删除项目temp、library文件夹,重新编译项目
- 重新编译引擎,重启项目
- 如果发现有问题也可以删除引擎目录bin/.cache文件夹,重新打开项目
我在我的机器上是实验成果的,各位坛友感兴趣可以尝试一下,有问题欢迎交流,另外附上我修改的完整文件。基于Creator3.8.5
我修改的文件完整内容:
engine.zip (927.3 KB)