分享一个新的spine 局部换装思路,使用类似动态合图方式更换局部贴图

此前在社区“白嫖”了不少大佬的干货,一直心怀感激。今天我也来贡献一份自己的经验,和大家分享一个全新的 Spine 局部换装思路。

我的做法是采用类似动态合图的方式来处理 Spine 的图集。对于需要替换的部位,我直接用小图覆盖到图集的对应位置。而且,整个过程不会对原始的 Spine 数据造成任何污染,保证了数据的纯净性。

话不多说,直接上代码,还请各位大佬品鉴:

testChangleSpine(spinePath: string, attName: string, texture: cc.Texture2D) {
    cc.resources.load(spinePath, sp.SkeletonData, (err, sk) => {
        if (err) {
            console.error('Failed to load costume data:', err);
            return;
        }

        let newSk = this.spineChangePart(sk, attName, texture); // 局部换装
        let node = new cc.Node();
        let spine = node.addComponent(sp.Skeleton);
        spine.premultipliedAlpha = false;
        spine.skeletonData = newSk;
        this.node.addChild(node); // 将新节点添加到当前节点下
        spine.setAnimation(0, 'run', true); // 播放动画
    });
}

/**
 *  局部换装
 *  @param sk 原始骨骼数据
 *  @param attName 附件名称
 *  @param texture 替换的小图片
 */
spineChangePart(sk: sp.SkeletonData, attName: string, texture: cc.Texture2D) {
    let date = new Date();
    let newSk = new sp.SkeletonData(); // 创建新的骨骼数据,不污染原始数据方式。

    // sk._getAtlas(); ( 原生直接报错,无法使用,求大佬补全)
    // let region = sk._atlasCache.findRegion(attName); // 查找附件的 region
    // @ts-ignore
    newSk._uuid = sk._uuid + "_" + date.getTime() + "_copy"
    newSk.skeletonJson = sk.skeletonJson;

    // 处理二进制数据
    // @ts-ignore
    if (sk._nativeAsset) {
        // @ts-ignore
        newSk._nativeAsset = sk._nativeAsset;
        // @ts-ignore
        newSk._nativeUrl = sk.nativeUrl;
    }

    newSk.atlasText = sk.atlasText;
    // @ts-ignore
    newSk.textureNames = sk.textureNames; // 替换纹理名称

    let orginTex: cc.Texture2D = sk.textures[0]; // 获取原始纹理
    let newTex = new cc.RenderTexture(); // 创建新纹理
    newTex.initWithSize(orginTex.width, orginTex.height); // 初始化新纹理

    //@ts-ignore
    newTex.drawTextureAt(orginTex, 0, 0); // 将原始纹理复制到新纹理上

    let regionPos = this.getRegionXY(attName, sk.atlasText); // 解析atlas字符串获取附件的坐标
    // @ts-ignore
    newTex.drawTextureAt(texture, regionPos.x, regionPos.y); // 将小图纹理绘制到指定位置

    newSk.textures[0] = newTex; // 设置纹理
    return newSk;
}

/**
 * 解析附件的坐标数据 sk._getAtlas() 无法使用,所以只能自己解析
 * @param attName
 * @param atlasText
 * @returns
 */
getRegionXY(attName: string, atlasText: string) {
    const lines = atlasText.split('\n');
    let foundStatus = false;
    let pos = cc.v2();

    for (let i = 0; i < lines.length; i++) {
        const line = lines[i].trim();
        if (line === attName) {
            foundStatus = true;
        } else if (foundStatus && line.startsWith("xy:")) {
            const xyValues = line.split(":")[1].trim();
            let p = xyValues.split(",");
            pos.x = parseFloat(p[0]);
            pos.y = parseFloat(p[1]);
            break;
        }
    }

    return pos;
}

在不再使用 Spine 动画节点时,应该手动释放相关资源,避免内存泄漏
// 移除节点
node.destroy();

// 释放资源
cc.assetManager.releaseAsset(newSk);
cc.assetManager.releaseAsset(newSk.textures[0]);

希望这段代码能给大家带来一些启发,如果有任何问题或者建议,欢迎各位大佬在评论区留言交流!

16赞

为开源精神点赞!!!!!!!!!

1赞

来一张效果图,直观感受下,网页,原生皆可用。

mask1

DC会增加吗

点个赞,谢谢你的分享精神

dc 不会增多

有demo就更好了,还是给你点赞:+1:t2:

提示一点,如果有多种换装方案且包含组合形式,那么你将生成超级多的RT

这种看需求,如果只针对当前角色换装,你直接对原始贴图进行处理,不创建RT,任意部位都可以使用小贴图更换,随意组合.

思路奇特,涨姿势了

大佬如果有空做个demo上git,这不得得到很多星啊

spine换装mark!

大佬,连续换同一个spine会出现除了替换部分其余部分消失的问题,但是如果一开始保存了spine的skeletonData就不会有问题

mark!

mark!