cocos2dx-lua的spine局部换装之进阶篇

来源:互联网 发布:tensorflow源码语言 编辑:程序博客网 时间:2024/05/22 17:12

之前写了一篇文章关于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, constchar* attachmentName) {

if (slotIndex == -1)return0;

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, constchar* attachmentName) {

if (slotIndex == -1)return0;

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,替换时单独判断。









0 0
原创粉丝点击