語文老師說,文章要有個好開頭!!!
最近正在引入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()方法下出現了某段代碼:
1 for (int i = 0, n = skeleton->slotsCount; i < n; i++) { 2 spSlot* slot = skeleton->drawOrder[i]; 3 if (!slot->attachment) continue; 4 CCTexture2D *texture = nullptr; 5 switch (slot->attachment->type) { 6 case SP_ATTACHMENT_REGION: { 7 spRegionAttachment* attachment = (spRegionAttachment*)slot->attachment; 8 spRegionAttachment_computeWorldVertices(attachment, slot->bone, worldVertices); 9 texture = getTexture(attachment); 10 uvs = attachment->uvs; 11 verticesCount = 8; 12 triangles = quadTriangles; 13 trianglesCount = 6; 14 r = attachment->r; 15 g = attachment->g; 16 b = attachment->b; 17 a = attachment->a; 18 break; 19 } 20 case SP_ATTACHMENT_MESH: { 21 spMeshAttachment* attachment = (spMeshAttachment*)slot->attachment; 22 spMeshAttachment_computeWorldVertices(attachment, slot, worldVertices); 23 texture = getTexture(attachment); 24 uvs = attachment->uvs; 25 verticesCount = attachment->verticesCount; 26 triangles = attachment->triangles; 27 trianglesCount = attachment->trianglesCount; 28 r = attachment->r; 29 g = attachment->g; 30 b = attachment->b; 31 a = attachment->a; 32 break; 33 } 34 case SP_ATTACHMENT_SKINNED_MESH: { 35 spSkinnedMeshAttachment* attachment = (spSkinnedMeshAttachment*)slot->attachment; 36 spSkinnedMeshAttachment_computeWorldVertices(attachment, slot, worldVertices); 37 texture = getTexture(attachment); 38 uvs = attachment->uvs; 39 verticesCount = attachment->uvsCount; 40 triangles = attachment->triangles; 41 trianglesCount = attachment->trianglesCount; 42 r = attachment->r; 43 g = attachment->g; 44 b = attachment->b; 45 a = attachment->a; 46 break; 47 } 48 } 49 if (texture) { 50 if (slot->data->blendMode != blendMode) { 51 batch->flush(); 52 blendMode = slot->data->blendMode; 53 switch (slot->data->blendMode) { 54 case SP_BLEND_MODE_ADDITIVE: 55 ccGLBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE); 56 break; 57 case SP_BLEND_MODE_MULTIPLY: 58 ccGLBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA); 59 break; 60 case SP_BLEND_MODE_SCREEN: 61 ccGLBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); 62 break; 63 default: 64 ccGLBlendFunc(blendFunc.src, blendFunc.dst); 65 } 66 } 67 color.a = skeleton->a * slot->a * a * 255; 68 float multiplier = premultipliedAlpha ? color.a : 255; 69 color.r = skeleton->r * slot->r * r * multiplier; 70 color.g = skeleton->g * slot->g * g * multiplier; 71 color.b = skeleton->b * slot->b * b * multiplier; 72 batch->add(texture, worldVertices, uvs, verticesCount, triangles, trianglesCount, &color); 73 } 74 } 75 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