cocos2d下,优秀骨骼spine的换装思路

语文老师说,文章要有个好开头!!!
最近正在引入spine骨骼代替dragon bone骨骼,既然要替代,那么原先在dragon bone上的一些额外需求,不管dragon bone上能不能实现,都应该在spine上尝试一番.
说带换装,spine自带的皮肤可以实现整体换装,这个应该不用介绍,setSkin一下就ok了.但是,策划往往会需要用到局部换装,一种情况下是该部件本身存在于皮肤下,这种情况下,只要能得到目标skin,目标slot,拿到目标相应的attachment , 替换一下即可 。

///////////////////////
//替换另一个皮肤下的某个部件
//for (int i = 0; i < skeleton->data->skinsCount; i++)
//{
//  if(! strcmp(skins[i]->name,"goblingirl"))
//  {
//  int index = spSkeleton_findSlotIndex(skeleton,"head");
//  attachment = spSkin_getAttachment(skins[i],index,"head");
//  spSlot_setAttachment(goblin->findSlot("head"),attachment);
//  }
//}
//////////////////////

方法不止一种,但思路都是一样的,这样就解决了皮肤间单独换装的问题。
最后一种最坑爹的需求就是希望能把一个自己的纹理放到骨骼中的某个部位。这个需求当时去网上找并没有找到什么有效的方法。自己摸索了一下大致是做出来了。
首先顺藤摸瓜,看看SkeletonRenderer是怎么渲染出整个骨骼动画的。在draw()方法下出现了某段代码:

for (int i = 0, n = skeleton->slotsCount; i < n; i++) {
        spSlot* slot = skeleton->drawOrder[i];
        if (!slot->attachment) continue;
        CCTexture2D *texture = nullptr;
        switch (slot->attachment->type) {
        case SP_ATTACHMENT_REGION: {
            spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment;
            spRegionAttachment_computeWorldVertices(attachment, slot->bone, worldVertices);
            texture = getTexture(attachment);
            uvs = attachment->uvs;
            verticesCount = 8;
            triangles = quadTriangles;
            trianglesCount = 6;
            r = attachment->r;
            g = attachment->g;
            b = attachment->b;
            a = attachment->a;
            break;
        }
        case SP_ATTACHMENT_MESH: {
            spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment;
            spMeshAttachment_computeWorldVertices(attachment, slot, worldVertices);
            texture = getTexture(attachment);
            uvs = attachment->uvs;
            verticesCount = attachment->verticesCount;
            triangles = attachment->triangles;
            trianglesCount = attachment->trianglesCount;
            r = attachment->r;
            g = attachment->g;
            b = attachment->b;
            a = attachment->a;
            break;
        }
        case SP_ATTACHMENT_SKINNED_MESH: {
            spSkinnedMeshAttachment* attachment = (spSkinnedMeshAttachment*)slot->attachment;
            spSkinnedMeshAttachment_computeWorldVertices(attachment, slot, worldVertices);
            texture = getTexture(attachment);
            uvs = attachment->uvs;
            verticesCount = attachment->uvsCount;
            triangles = attachment->triangles;
            trianglesCount = attachment->trianglesCount;
            r = attachment->r;
            g = attachment->g;
            b = attachment->b;
            a = attachment->a;
            break;
        }
        }
        if (texture) {
            if (slot->data->blendMode != blendMode) {
                batch->flush();
                blendMode = slot->data->blendMode;
                switch (slot->data->blendMode) {
                case SP_BLEND_MODE_ADDITIVE:
                    ccGLBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE);
                    break;
                case SP_BLEND_MODE_MULTIPLY:
                    ccGLBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
                    break;
                case SP_BLEND_MODE_SCREEN:
                    ccGLBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
                    break;
                default:
                    ccGLBlendFunc(blendFunc.src, blendFunc.dst);
                }
            }
            color.a = skeleton->a * slot->a * a * 255;
            float multiplier = premultipliedAlpha ? color.a : 255;
            color.r = skeleton->r * slot->r * r * multiplier;
            color.g = skeleton->g * slot->g * g * multiplier;
            color.b = skeleton->b * slot->b * b * multiplier;
            batch->add(texture, worldVertices, uvs, verticesCount, triangles, trianglesCount, &color);
        }
    }
    batch->flush();

代码比较长,其实是遍历了所有slot,取出其下的attachment,根据type属性强转成对应类型的attachment计算或取出不同类型attachment渲染所需要的数据然后取出纹理根据数据进行渲染。
比较关键的一步是取出纹理,看到这步仿佛看到了春天,于是我写代码 把这个纹理取出来,创建成精灵显示在场景下时才发现,attachment->rendererObject->page->rendererObject 这个东西根本不是一个部位的纹理,所有部位的纹理取出来都是纹理集整个纹理,也就是像下面这样的大纹理。


其他平台的情况本人并不是很清楚,但是在cocos2d-x下,spine的骨骼动画是一个node读取了大量的数据,一个纹理渲染出来,所以并不存在可以获得单个纹理的接口。spine并没有专门对cocos2d-x做处理,而是从一大堆C语言写的代码上加了一个Node就完成了。
在此坑爹的情况下,此时的我是崩溃的。


我去spine详细地读了那篇用English写的简单文档.结合底层坑爹C语言代码后发现attachment->rendererObject实际上存放的是区域信息AtlasRegion,而Atlas Region下page则是页信息,page再向上一级就是最大的atlas,一个骨骼有一个atlas(暂时的想法是这样子的)而一个atlas有多个page,上图纹理集就是一个page,所以理论上而言可以用代码定义一个并不存在的page 将图片信息写入page->region->rendererObject,再将region定义出一个attachment放入slot中,渲染时就会顺着attachment->rendererObject->page->rendererObject一条线路读取到我们所放入的纹理。而其中复杂的数据如果顺序错误或遗漏则会引起渲染的结果畸形。

CCTexture2D* pTexture = CCTextureCache::sharedTextureCache()->addImage("CloseSelected.png");
    int attachmentType = slot->attachment->type;;
    switch (attachmentType)
    {
    case SP_ATTACHMENT_REGION:{
        
        

        break;}
    case SP_ATTACHMENT_MESH:{
        spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment;
        spAtlas* atlas = goblin->getAtlas();
        spAtlasPage* page =  atlas->pages;
        spAtlasPage* newPage = spAtlasPage_create(atlas , "goblins-test.png");
        spAtlasRegion* region = ((spAtlasRegion*)attachment->rendererObject);
        spAtlasRegion* newRegion =  spAtlasRegion_create();
        CCTexture2D* newTexture = CCTextureCache::sharedTextureCache()->addImage("goblin-test4.png");
        newPage->format = page->format;
        newPage->magFilter = page->magFilter;
        newPage->minFilter = page->minFilter;
        newPage->name = page->name;
        newPage->rendererObject = newTexture;
        //newPage->rendererObject = NULL;
        newPage->width = 40;
        newPage->height = 40;
        newPage->uWrap = page->uWrap;
        newPage->vWrap = page->vWrap;
        newRegion->height= newPage->height;
        newRegion->width = newRegion->width;
        newRegion->offsetX = 0;
        newRegion->offsetY = 0;
        newRegion->originalHeight = newRegion->height;
        newRegion->originalWidth = newRegion->width;
        newRegion->name = region->name;
        newRegion->u = 0;
        newRegion->v = 0;
        newRegion->u2 = 1;
        newRegion->v2 = 1;
        newRegion->page = newPage;
        attachment->rendererObject = newRegion;
        attachment->regionU = newRegion->u;
        attachment->regionV = newRegion->v;
        attachment->regionU2 = newRegion->u2;
        attachment->regionV2 = newRegion->v2;
        attachment->regionRotate = newRegion->rotate;
        attachment->regionOffsetX = newRegion->offsetX;
        attachment->regionOffsetY = newRegion->offsetY;
        attachment->regionWidth = newRegion->width;
        attachment->regionHeight = newRegion->height;
        attachment->regionOriginalWidth = newRegion->originalWidth;
        attachment->regionOriginalHeight = newRegion->originalHeight;
        attachment->regionHeight = newRegion->height;

        spMeshAttachment_updateUVs(attachment);


        break;}
    case SP_ATTACHMENT_SKINNED_MESH:{
        spSkinnedMeshAttachment* attachment = (spSkinnedMeshAttachment*)slot->attachment;
        ((spAtlasRegion*)attachment->rendererObject)->page->rendererObject = pTexture;

        break;}
    default:{
        CCLog("%s" , "undisposed attachment type !~~~~ ");
        break;}
    }

以上是其中一种类型的attachment,经过上述实验后,我自己做的纹理成功地被替换到骨骼中 并且一切动作都正常,蒙皮效果也是正常的,但是可能由于数据并不够完整,新加入的纹理必须和原来的骨骼中相应部位的纹理一样大小,其实这也是可以理解的,如果大小不一样,原来的蒙皮会扭曲新纹理,从而出现畸形,可能其他类型的attachment如region类型可以实现替换的图像大于原大小,但是这种类型没有蒙皮效果也就是没有Mesh网格渲染效果,不够柔软,这样就失去了spine的特色。


--MT.Team
--MT.Lambda

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容