日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

lua 使用 spine 的一些问题

發布時間:2025/4/5 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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。基本寫法如下:

    ?

    [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/*bool*/loop)?{??
  • ????_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 的一些问题的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。