生活随笔
收集整理的這篇文章主要介紹了
lua 使用 spine 的一些问题
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文轉自http://blog.csdn.net/dinko321/article/details/44176041
?
一、基本使用:
?
[plain]?view plaincopy
self.skeletonNode?=?sp.SkeletonAnimation:create("sptest/spineboy.json",?"sptest/spineboy.atlas",?0.3)???self.skeletonNode:setAnimation(0,?"walk",?true)??????self.skeletonNode:setMix("walk",?"jump",?0.2)???self.skeletonNode:setMix("jump",?"run",?0.2)??????self.skeletonNode:setAnimation(0,?"walk",?false)???self.skeletonNode:addAnimation(0,?"hit",?false)???????self.skeletonNode:setDebugBonesEnabled(true)?????self.skeletonNode:registerSpineEventHandler(function?(event)???????print(string.format("[spine]?%d?start:?%s",?event.trackIndex,event.animation))???end,?sp.EventType.ANIMATION_START)?? ?
?
1、一開始,要set一個animation,第一個參數是track(后面細說)。
2、播放其他動畫是用add
3、當set的動畫播放完畢停止之后(非循環),再add動畫是不會動的。
4、mix 就是讓2個動畫直接的銜接更平滑,最后一個參數是過渡時間
?
二、track的概念
做音頻的有個概念,叫音軌,就是track,基本比如錄歌,有個主唱,有和聲,有吉他,有貝斯。那么把這些分別錄制,再一起放出來,就是一首歌,分別錄制的這些,就是一個個獨立的音軌,一起放出來就是我們最后聽到的歌曲。
對比PS,這個概念叫圖層。
所以其實可以把不同的動作放到不同的track中去做。
?
三、回調中的處理以及bug
1、回調
正常情況下,都會對一個動畫的完結做一些處理。比如我需要的就是,當動作A完成后,播放動作C?;緦懛ㄈ缦?#xff1a;
?
[plain]?view plaincopy
self.skeletonNode:registerSpineEventHandler(function?(event)?????????if?event.animation?==?"hit"?then??????????print("======?hit?end?====")??????end??end,?sp.EventType.ANIMATION_END)?? 現在的問題是,我需要skeletonNode才能播放這個動畫。怎么才能拿到這個skeletonNode,想當然的以為應該這么寫
?
?
[plain]?view plaincopy
self.skeletonNode:registerSpineEventHandler(function?(event)??????if?event.animation?==?"hit"?then??????????self.skeletonNode:setAnimation(0,?"hit",?false)??????end????end,?sp.EventType.ANIMATION_END)?? 試驗發現可行,但是依據是什么呢?追蹤發現,這個地方是調用的一個Cpp的綁定,找到lua_cocos2dx_spine_manual.cpp如下
[cpp]?view plaincopy
int?tolua_Cocos2d_CCSkeletonAnimation_registerSpineEventHandler00(lua_State*?tolua_S)??{??????{??????????LuaSkeletonAnimation*?self????=?(LuaSkeletonAnimation*)??tolua_tousertype(tolua_S,1,0);??????????if?(NULL?!=?self?)?{??????????????int?handler?=?(??toluafix_ref_function(tolua_S,2,0));??????????????spEventType?eventType?=?static_cast<spEventType>((int)tolua_tonumber(tolua_S,?3,?0));????????????????????????????switch?(eventType)?{??????????????????case?spEventType::SP_ANIMATION_END:??????????????????????{??????????????????????????self->setEndListener([=](int?trackIndex){??????????????????????????????executeSpineEvent(self,?handler,?eventType,?trackIndex);??????????????????????????});??????????????????????????ScriptHandlerMgr::getInstance()->addObjectHandler((void*)self,?handler,?ScriptHandlerMgr::HandlerType::EVENT_SPINE_ANIMATION_END);??????????????????????}??????????????????????break;??????????????????default:??????????????????????break;??????????????}??????????}??????}??????return?0;??}?? 代碼舍棄了一些無關緊要的細節。然后就會發現,這里取了3個值,一個是self,一個是handler,就是我們傳入的回調函數,還有一個eventType,然后用Cpp注冊了這個回調,回調觸發的時候,執行了executeSpineEvent,此函數如下:
?
?
[cpp]?view plaincopy
int?executeSpineEvent(LuaSkeletonAnimation*?skeletonAnimation,?int?handler,?spEventType?eventType,?int?trackIndex?,?int?loopCount?=?0,?spEvent*?event?=?nullptr?)??{??????int?ret?=?0;????????????spTrackEntry*?entry?=?spAnimationState_getCurrent(skeletonAnimation->getState(),?trackIndex);??????std::string?animationName?=?(entry?&&?entry->animation)???entry->animation->name?:?"";??????std::string?eventTypeName?=?"";????????????switch?(eventType)?{??????????case?spEventType::SP_ANIMATION_END:??????????????{??????????????????eventTypeName?=?"end";??????????????}??????????????break;??????????default:??????????????break;??????}????????????LuaValueDict?spineEvent;??????spineEvent.insert(spineEvent.end(),?LuaValueDict::value_type("type",?LuaValue::stringValue(eventTypeName)));??????spineEvent.insert(spineEvent.end(),?LuaValueDict::value_type("trackIndex",?LuaValue::intValue(trackIndex)));??????spineEvent.insert(spineEvent.end(),?LuaValueDict::value_type("animation",?LuaValue::stringValue(animationName)));??????spineEvent.insert(spineEvent.end(),?LuaValueDict::value_type("loopCount",?LuaValue::intValue(loopCount)));????????????stack->pushLuaValueDict(spineEvent);??????ret?=?stack->executeFunctionByHandler(handler,?1);??????return?ret;??}?? 然后結合quick里面關于handler的實現,類似cocos2dx在C++11以前的實現,所以這個地方是obj:function()的調用方法,這個obj就是self。所以其實回調的寫法完整的應該是:
[plain]?view plaincopy
self.skeletonNode:registerSpineEventHandler(handler(self,function?(self,event)??????if?event.animation?==?"hit"?then??????????self.skeletonNode:setAnimation(0,?"walk",?true)??????end??????end),?sp.EventType.ANIMATION_END)?? ?
至于怎么隱式轉換實現的這個function到handler,我還不知道。。。。。。希望知道的告知。。。。
2、bug
然后就會發現,這么寫,程序會蹦掉,回調會不停的觸發。這么寫,指的是在回調里面setAnimation。后面面會說解法,此部分可以直接跳過。
從lua是找不出問題了,直接去C++。
在C++里面寫一個類似的程序,在回調里面調用setAnimation,會發現如下調用關系。
回調調用set:
[cpp]?view plaincopy
skeletonNode->setEndListener(?[this]?(int?trackIndex)?{?????????spTrackEntry*?entry?=?spAnimationState_getCurrent(skeletonNode->getState(),?trackIndex);?????????const?char*?animationName?=?(entry?&&?entry->animation)???entry->animation->name?:?0;?????????if?(strcmp(animationName,?"hit")==0)?????????{?????????????skeletonNode->setAnimation(0,?"hit",?false);?????????}??});?? node的set的實現:
?
?
[cpp]?view plaincopy
spTrackEntry*?SkeletonAnimation::setAnimation?(int?trackIndex,?const?std::string&?name,?bool?loop)?{??????spAnimation*?animation?=?spSkeletonData_findAnimation(_skeleton->data,?name.c_str());??????if?(!animation)?{??????????log("Spine:?Animation?not?found:?%s",?name.c_str());??????????return?0;??????}??????return?spAnimationState_setAnimation(_state,?trackIndex,?animation,?loop);??}?? 上面那個是通過名字取了動畫,然后進入下一級調用:
?
?
[cpp]?view plaincopy
spTrackEntry*?spAnimationState_setAnimation?(spAnimationState*?self,?int?trackIndex,?spAnimation*?animation,?int????_spAnimationState*?internal?=?SUB_CAST(_spAnimationState,?self);????????spTrackEntry*?entry;??????spTrackEntry*?current?=?_spAnimationState_expandToIndex(self,?trackIndex);??????if?(current)?_spAnimationState_disposeAllEntries(self,?current->next);??<span?style="white-space:pre">????</span>...?...??}?? 后面的選擇性忽略了,因為在if這里就蹦了,然后一路追下去,會發現是最后在free的時候,free了一個沒有alloc的指針,這個指針就是上面的current->next 。
這個bug,從目前的情況看,大概就是,set新的animation的時候,會把當前的animation做清理,清理的是current的next,因為current已經播放,結果這個next空了。
?
至于為什么空,不知道,太麻煩了,空了看。。。
?
3、關于2的解法
利用前面說的track,換一個track。
之前說了,bug的主要原因是當前的track的動畫需要做清理,只要不在已經setAnimation的track上set新的就行了。
換個track,set或者add都可以。
?
4、補充解法:
又跟了一下,發現現象很詭異,
不過如果在end回調里面,用定時器,把操作延時0.001秒或者隨便多少反正就是延時到下一幀去做,就可以正常。
?
?
未驗證問題,還沒弄清楚的:
1、不同的track動畫直接的的mix效果
2、function到handler的隱式轉化
3、spine的free的next的錯誤的根源
轉載于:https://www.cnblogs.com/liuqing0328/p/4900884.html
總結
以上是生活随笔為你收集整理的lua 使用 spine 的一些问题的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。