1. 程式人生 > >cocos2dx-lua的spine區域性換裝之進階篇

cocos2dx-lua的spine區域性換裝之進階篇

之前寫了一篇文章關於cocos2dx-lua的spine區域性換裝的。主要原理就是根據cocos2dx中SpineRenderer.cpp中setSkin()方法,添加了addSkin()方法。並在

lua_cocos2dx_spine_auto.cpp中新增方法 int lua_cocos2dx_spine_SkeletonRenderer_addSkin(la_State* tolua_S),完成lua-building,使在lua端可以呼叫。

上一篇地址:http://blog.csdn.net/u010536615/article/details/51137235

寫進階篇的原因是:

Spine動畫製作時有個關鍵幀的東西,一般做動畫時第一幀就要把所有的相關部件(slots)等都key上關鍵幀。然而我們之前做動畫時並沒key上關鍵幀,所以之前addSkin()方法沒有任何問題。但將關鍵幀key上之後,儘管方法實現沒有任何問題,但addSkin()方法不管用了。於是就把spine的從解析到動畫實現的程式碼都看了一遍。使方法支援key上關鍵幀後的區域性換裝。

解決方法直接看標題3。

---------------------------------------------------------------1.換裝原理--------------------------------------------

更換slots插槽中的attachment部件。

具體是找到對應slots的attachment部件,然後遍歷skins中當前skin是否有該attachment部件,如果有,將該部件替換掉原slots中的attachment部件。

---------------------------------------------------------------2.問題產生原因

--------------------------------------------

-------原因:

    self.actor = sp.SkeletonAnimation:create("red/red.json", "red/red.atlas", 1)
    self.actor:setAnimation(0, "red_wait", true)
    self.actor:setPosition(450, 300)
    self:addChild(self.actor)

其實只建立,不掉用動畫self.actor:setAnimation(0, "red_wait", true),addSkin()方法是好用的。

那麼問題就出在播放動畫的時候。播放動畫時會重新根據當前的skin替換掉slots中的attachment。所以之前設定的addSkin()中替換的,當動畫動起來時就不起作用了。

-------引擎帶的setSkin()起作用:

那為什麼setSkin()是好用的呢。這就是關鍵。

之前也說了,替換的時候是找到當前設定的skin。而引擎的邏輯中只能設定整體的skin,也就是整套換裝。沒有提供設定單個部件skin的功能。

setSkin方法中通過

CONST_CAST(spSkin*, self->skin) = newSkin;

來設定當前的整體裝備。

通過當前面板去替換每一個部件:

if (self->skin) {

spAttachment *attachment =spSkin_getAttachment(self->skin, slotIndex, attachmentName);

if (attachment)return attachment;

}

if (self->data->defaultSkin) {

spAttachment *attachment =spSkin_getAttachment(self->data->defaultSkin, slotIndex, attachmentName);

if (attachment)return attachment;

}

----所以我們需要做的就是給每一個slot都加上skin的屬性。

替換時查詢當前部件是否有skin,有的話應用該部件的skin。而不用整體的skin。

--------------------------------------3.解決方法--------------------------------------------

1.為每一個slot都加上skin的屬性

editor-support->spine->SlotData.h中spSlotData結構體中增加spSkin*const skin屬性並附上初始值。

新增Skin.h  #include <spine/Skin.h>

typedef struct spSlotData {

constchar* const name;

const spBoneData* const boneData;

const char* attachmentName;

float r, g, b, a;

spBlendMode blendMode;

    spSkin* const skin;//新增skin屬性

#ifdef __cplusplus

spSlotData() :

name(0),

boneData(0),

attachmentName(0),

        skin(0),//新增skin屬性

r(0), g(0), b(0), a(0),

blendMode(SP_BLEND_MODE_NORMAL) {

}

#endif

} spSlotData;

2.addSkin()方法中,給替換的對應slot附上skin

editor-support->spine->Skeleton.c中修改我上一篇中的spSkeleton_addSkin方法:

void spSkeleton_addSkin (spSkeleton* self,spSkin* newSkin, constchar* partName) {

    if (newSkin) {

        int i;

        for (i = 0; i < self->slotsCount; ++i) {

            spSlot* slot = self->slots[i];

            if (slot->data->attachmentName) {

                if (strcmp(slot->data->attachmentName, partName) ==0) {

                    spAttachment* attachment =spSkin_getAttachment(newSkin, i, slot->data->attachmentName);

                    if (attachment)

                    {

                        CONST_CAST(spSkin*, self->data->slots[i]->skin) = newSkin;//slot->skin賦值

                        spSlot_setAttachment(slot, attachment);

                    }

                }

            }

        }

    }

}

3.替換slot的attachment部件時判斷當前slot的skin
修改editor-support->spine->Skeleton.c中spSkeleton_getAttachmentForSlotIndex方法,其中兩個if是新增程式碼。

spAttachment* spSkeleton_getAttachmentForSlotIndex (constspSkeleton* self, int slotIndex, const char* attachmentName) {

if (slotIndex == -1)return 0;

if (self->skin) {

        //新增

        if (self->data->slots[slotIndex]->skin) {

            spAttachment *attachment =spSkin_getAttachment(self->data->slots[slotIndex]->skin, slotIndex, attachmentName);

            if (attachment)return attachment;

        }

spAttachment *attachment =spSkin_getAttachment(self->skin, slotIndex, attachmentName);

if (attachment)return attachment;

}

if (self->data->defaultSkin) {

                //新增

        if (self->data->slots[slotIndex]->skin) {

            spAttachment *attachment =spSkin_getAttachment(self->data->slots[slotIndex]->skin, slotIndex, attachmentName);

            if (attachment)return attachment;

       }

spAttachment *attachment =spSkin_getAttachment(self->data->defaultSkin, slotIndex, attachmentName);

if (attachment)return attachment;

}

return0;

}

4.應用

其中setSkin方法用來對比檢測用。

    self.actor = sp.SkeletonAnimation:create("red/red.json", "red/red.atlas", 1)
    self.actor:setAnimation(0, "red_wait", true)
    self.actor:setPosition(450, 300)
    self:addChild(self.actor)


    self.actor:setSkin("HM_LV1")
    local fashionStr = string.format("HM_LV%d", 4)
    self.actor:addSkin("HM_LV2", "weap_wuqi")
    self.actor:addSkin(fashionStr, "HM_huwan_z")
    self.actor:addSkin(fashionStr, "HM_R_jiao")
    self.actor:addSkin(fashionStr, "HM_jiao_L")--HM_L_jiao
    self.actor:addSkin(fashionStr, "HM_dun")
    self.actor:addSkin(fashionStr, "HM_kuzi")

--------------------------------------4.說明&備註--------------------------------------------

-------首先附上動畫key和不key關鍵幀後json檔案的區別:

key上:

"animations": {"red_wait": {"slots": {

                                "HM_R_jiao": {"attachment": [
{ "time": 0, "name": "HM_R_jiao" },
{ "time": 0.6, "name": null }],
"color": [
{ "time": 0, "color": "ffffffff" },
{ "time": 0.3, "color": "00ffffff" }
]}}}

不key上:

"animations": {"red_wait": {"slots": {


]}}}

其實就是key上了就有slots"HM_R_jiao"的屬性,關鍵就在attachment屬性,其實去掉attachment屬性,只保留color屬性,之前的addSkin()區域性換裝也是好用的。

-------動畫程式碼:

SkeletonAnimation.cpp

void SkeletonAnimation::update (float deltaTime) {

super::update(deltaTime);

deltaTime *= _timeScale;

spAnimationState_update(_state, deltaTime);

spAnimationState_apply(_state, _skeleton);

spSkeleton_updateWorldTransform(_skeleton);

}

這段程式碼看看就行,重點在下面。

-------解析上一段的程式碼的attachment屬性:

SkeletonJson.c檔案中的_spSkeletonJson_readAnimation方法:

 else if (strcmp(timelineArray->name, "attachment") == 0) {

spAttachmentTimeline *timeline = spAttachmentTimeline_create(timelineArray->size);

timeline->slotIndex = slotIndex;

for (frame = timelineArray->child, i = 0; frame; frame = frame->next, ++i) {

Json* name = Json_getItem(frame, "name");

spAttachmentTimeline_setFrame(timeline, i, Json_getFloat(frame, "time", 0),

name->type == Json_NULL ? 0 : name->valueString);

}

animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline);

duration = timeline->frames[timelineArray->size - 1];

if (duration > animation->duration) animation->duration = duration;

}

-------其中spAttachmentTimeline_create(timelineArray->size)

spAttachmentTimeline* spAttachmentTimeline_create (int framesCount) {

spAttachmentTimeline* self =NEW(spAttachmentTimeline);

_spTimeline_init(SUPER(self),SP_TIMELINE_ATTACHMENT,_spAttachmentTimeline_dispose,_spAttachmentTimeline_apply);

CONST_CAST(int, self->framesCount) = framesCount;

CONST_CAST(float*, self->frames) =CALLOC(float, framesCount);

CONST_CAST(char**, self->attachmentNames) =CALLOC(char*, framesCount);

return self;

}

-------關鍵就在_spAttachmentTimeline_apply動畫應用attachment的方法

Animation.c中的_spAttachmentTimeline_apply()方法。

void _spAttachmentTimeline_apply (constspTimeline* timeline,spSkeleton* skeleton, float lastTime, float time,

spEvent** firedEvents,int* eventsCount,float alpha) {

int frameIndex;

const char* attachmentName;

spAttachmentTimeline* self = (spAttachmentTimeline*)timeline;

if (time < self->frames[0]) {

if (lastTime > time)_spAttachmentTimeline_apply(timeline, skeleton, lastTime, (float)INT_MAX,0,0, 0);

return;

} else if (lastTime > time) /**/

lastTime = -1;

frameIndex = time >= self->frames[self->framesCount -1] ?

self->framesCount -1 :binarySearch1(self->frames, self->framesCount, time) -1;

if (self->frames[frameIndex] < lastTime)return;

attachmentName = self->attachmentNames[frameIndex];

        spSlot_setAttachment(skeleton->slots[self->slotIndex],

                            attachmentName ? spSkeleton_getAttachmentForSlotIndex(skeleton, self->slotIndex, attachmentName) :0);

}

--------重點:

最後一句是key上關鍵幀後addSkin()方法失效的關鍵。這句就是替換slot中的attachment部件。

    spSlot_setAttachment(skeleton->slots[self->slotIndex],

                        attachmentName ? spSkeleton_getAttachmentForSlotIndex(skeleton, self->slotIndex, attachmentName) : 0);

--------重點重點spSkeleton_getAttachmentForSlotIndex方法

spAttachment* spSkeleton_getAttachmentForSlotIndex (constspSkeleton* self, int slotIndex, const char* attachmentName) {

if (slotIndex == -1)return 0;

if (self->skin) {

spAttachment *attachment =spSkin_getAttachment(self->skin, slotIndex, attachmentName);

if (attachment)return attachment;

}

if (self->data->defaultSkin) {

spAttachment *attachment =spSkin_getAttachment(self->data->defaultSkin, slotIndex, attachmentName);

if (attachment)return attachment;

}

return0;

}

--------問題所在

其中的self->skin是skeleton的屬性,所有部件只有統一的一個。每當動畫播放時,就用這統一的skin去替換掉所有的skin。所以想區域性換裝就要紀錄每個slot的skin,替換時單獨判斷。