Spine的使用 With Cocos2d-x
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
Spine的使用(With Cocos2d-x)
Spine是一個2D的骨骼動畫編輯器, 因為其良好的UI設計及完整的功能, 在kickstarter上釋出以後立即收到追捧, 作為一個幾乎只有遊戲開發者才會使用的小眾工具, 募集了遠超目標5倍的資金, 共計6.7W多美元. 我在其專案釋出後, 成為了Spine在kickstarter的早一批backer, 這是我在kickstarter上第一個, 也是目前唯一一個支援的專案. 隨後, 通過不斷收到的郵件見證了Spine逐步完善的過程, 直到其發出target完成的郵件. 又過了這麼長時間了, 因為手頭的專案一直不需要太複雜的2D骨骼動畫, 拖著沒有研究, 現在也是時候看看Spine了, 可惜的是, Spine的使用還有一系列的
目錄:
Runtime使用
簡單的迴圈動畫
編輯上看視訊教程吧, 只是打包檔案需要使用TexturePacker的libgdx的Data Format來匯出, 字尾改為atlas.
然後, 從例子中能學到的東西:
- 用
new CCSkeletonAnimation("test.json", "test.atlas");
來建立想要的Spine動畫物件. - 用
CCSkeletonAnimation
的setAnimation("anim_name", true);
來設定想要的動畫. - 需要將CCSkeletonAnimation物件按node一樣處理, 用addChild介面新增到parent node上. 並且物件可以當作普通的Node一樣來操作, 因為它實際就繼承自CCNodeRGBA.
比如在一個node中, 按如下程式碼可以建立一個spineboy行走的動畫, 並且迴圈播放.
skeletonNode = new CCSkeletonAnimation("spineboy.json", "spineboy.atlas");skeletonNode->setAnimation("walk", true);CCSize windowSize = CCDirector::sharedDirector()->getWinSize();skeletonNode->setPosition(ccp(windowSize.width / 2, 20));addChild(skeletonNode);skeletonNode->release();
播放一次動畫
簡單的情況, 播放一次動畫後就不管了, 那麼直接使用CCSkeletonAnimation::setAnimation
, 並且以false為第二個引數就好了.
更復雜的情況, 需要知道什麼時候這個動畫播放完了, 因為Spine的Runtime中沒有動畫結束的回撥(這是另外一種良好的設計), 只能通過在update中判斷. 更進一步的悲劇是, 在cocos2d-x的Runtime中中沒有簡單的判斷方法, example中給了一個方法:
if (skeletonNode->states[0]->loop) { if (skeletonNode->states[0]->time > 2) skeletonNode->setAnimation("jump", false);} else { if (skeletonNode->states[0]->time > 1) skeletonNode->setAnimation("walk", true);}
這裡使用的方法是判斷播放時間, 我對此方法表示強烈的反感, 也絕對的建議大家不要使用, 因為你不僅需要預先知道每個動畫播放的時長, 而且任何時候你改動了動畫的播放時間, 這個程式碼都得回來改, 這樣做根本就違反我們使用編輯器的初衷, 甚至我覺得在Runtime的example中給出這種程式碼是非常不負責任的行為. 這個方法只在一種情況下使用, 那就是你的確是想要在某個動畫播放的確定時間幹某個事情, 不過這種情況應該非常少見.
在的確需要知道播放完一次動畫時, 我建議用以下方式來完成, 因為沒有現成的C++介面, 這裡借用了一個C程式碼中的函式來完成工作:
if(AnimationState_isComplete(skeletonNode->states[0])) { if ( 0 == strcmp(skeletonNode->states[0]->animation->name, "walk") ) { skeletonNode->setAnimation("jump", false); } else { skeletonNode->setAnimation("walk", false); }}
連續播放動畫
上面那個例子中的動畫連續動畫播放可以直接通過CCSkeletonAnimation::addAnimation
介面來完成, 這樣更加簡單.
skeletonNode = new CCSkeletonAnimation("spineboy.json", "spineboy.atlas");skeletonNode->addAnimation("walk", false);skeletonNode->addAnimation("jump", false);skeletonNode->addAnimation("walk", true);
但是失去了一些靈活性, 並且, Spine還不支援將多個動畫接起來作為一個完整的動畫使用, 這個挺弱的, 再加上Spine工具本身就沒有連線多個動畫的功能, 就更加弱了.
程式控制的骨骼動畫
所謂程式控制的骨骼動畫, 就是類似Spine宣傳動畫中那樣, Globin的目光跟隨著滑鼠的移動. 這個功能的實現, 應該算是骨骼動畫中最酷的一部分了. 傳統的序列幀動畫完全無法實現, 要實現的話還是得在序列幀外拆分肢體, 然後單獨實現.
在骨骼動畫中, 可以直接取到骨骼, 然後調整骨骼, 實現這樣的效果, 要方便很多. 在Spine中取得骨骼的函式是CCSkeletonAnimation::findBone
, 其他的就是設定rotation就行了.
void ExampleLayer::ccTouchesMoved(CCSet *touches, CCEvent *event) { CCTouch* touch = (CCTouch*)touches->anyObject(); CCPoint pos = touch->getLocation(); Bone* head = skeletonNode->findBone("head"); CC_ASSERT(head != nullptr); float tanValue = (pos.y - head->worldY) / (pos.x - head->worldX); float rotation = atan(tanValue) * 180 / 3.1415; head->rotation = rotation;}
需要注意的是, 動畫本身要是在播放的話, 不能動這個骨骼(被parent牽引是OK的), 不然的話會被動畫本身強制改變, 看不到touch帶來的效果.
其他功能
慢動作和快動作
設定CCSkeletonAnimation的timeScale值.
動畫混合
對於一般情況下, 動畫的切換要求兩個動畫完全能銜接上, 不然會出現跳躍感, 這個對於美術來說要求很高, 而Spine加了個動畫混合的功能來解決這個問題. 使得不要求兩個動畫能完全的銜接上.
比如上面的walk和jump動畫, 就是銜接不上的, 直接按上面的辦法切換, 會出現跳躍, 但是加了混合後, 看起來就很自然了. 哪怕放慢10倍速度觀察, 也完美無缺. 這個功能在序列幀動畫時是無法實現的, 也是最體現Spine價值的一個功能. 程式碼如下:
skeletonNode->setMix("walk", "jump", 0.3f);skeletonNode->setMix("jump", "walk", 0.3f);
問題
Spine的出現, 對於2D骨骼動畫編輯工具來說, 絕對是翻天覆地的變化, 劃時代的. 這也再次說明了自己的問題, 因為和以前的同事說了很久了, 其實我們需要一個這樣的編輯器, 但是自己卻從來沒有寫過一個-_-! 當然, 我們當時討論的是做一個開源的. 但是, Spine畢竟剛剛出現, 其實還是有不少的問題. 如下:
工具使用上
- 體驗上, 因為Spine用了JAVA來實現偷懶的跨平臺, 很多地方都弱爆了, 奇怪的menu就不說了, 那檔案對話方塊難用的要死, 在Mac下, 會覺得那檔案對話方塊簡直就是折磨人, 不管是選對一個資料夾, 還是儲存檔案到一個地方, 都能讓人很鬱悶. 還能出現原介面被阻塞, 而檔案對話方塊被擋住的情況. 假如要一套程式碼的跨平臺通用, 那就沒有使用者體驗可言. 當然, 其實Spine本身對動畫編輯方面的體驗還是非常棒的.
- 功能上, Spine不支援像cocos builder一樣直接讀取pack後的材質, 只能讀原始的材質, 這個有些弱, 導致需要在原始資源上進行編輯. 這個在工程管理上沒有直接讀取打包後的材質方便. 假如打包出了什麼問題, 這裡也看不見.
- 還是功能上, Spine在編輯器中無法直接連線多個動畫, 這個功能連cocos builder中都有.
- 雖然Spine有個用example做UI的演示, 但是實際上因為Spine沒有任何地方可以設定點選響應和設定變數繫結, 這個基本上也就是隻能做做UI上面的動畫.
- 這個和Runtime也有關, Spine的擴充套件性幾乎沒有, 連想自定義一個引數都沒有辦法, 比如我想用Spine來設定一個Bone的旋轉的上限和下限, 也無法做到.
Runtime的問題
Spine的Runtime有些太馬虎了, 問題相當多.
- 沒有詳細的文件就算了, 甚至連程式碼的註釋生成文件都沒有. 同時代碼的註釋也少的可憐, 根本就不像一個嚴謹的開源專案. 更進一步, 連example都只有1個, 要是不知道該怎麼用, 哭去吧. 這個只能說Spine還不夠成熟, 一般來說, 像這種程度的東西, 最好別用.
- 為了讓一套程式碼能夠儘量支援多的引擎, 有些地方太偷懶了. 對此吐槽的也不止我一個, 比如這裡的A call for coders to build a better Spine runtime for Cocos2D, 就算是C++使用者, 用這簡單從C wrap過來的runtime我都感覺非常難受, 更加別說objc的使用者了.
- 最大的問題是Spine用了一套字尾為atlas的資原始檔, 但是這根本不是cocos2d/cocos2d-x的使用方式, 我們要的是plist! 所以Spine用到的資源會和Cocos2d-x中用到的資源格格不入, 無法統一管理和cache. 這種問題使得Spine幾乎不可用, 因為一個稍微想點樣子的遊戲, 也不能容忍每個動畫都是在需要播放的時候再載入資源.
這個問題有個解決方案, 就是不用example中使用的CCSkeletonAnimation
建立介面CCSkeletonAnimation::CCSkeletonAnimation (const char* skeletonDataFile, const char* atlasFile, float scale)
, 而是使用CCSkeletonAnimation::CCSkeletonAnimation (const char* skeletonDataFile, Atlas* atlas, float scale)
這個介面, 並且自己首先快取Atlas檔案. 或者, 直接快取所有可能出現的skeletonNode物件. 只是, 這些解決方法都太麻煩並且不夠優美. 並且, 還是沒有辦法和cocos2d-x原有的資源統一管理.