首先我們來看到底什麼是骨骼動畫:
在早期的機器上,渲染本身已經占用了很多CPU資源,是以,對于渲染,往往采取的是一種空間換時間的政策,以避免在模型的渲染中繼續加重CPU的負擔。
幀動畫模型在這種條件下應運而生。比較著名的幀動畫格式是Quake2所采用的MD2。
到今天為止,幀動畫依然存在,隻不過幀動畫更多地是來描述小且動作相對少些的物體。
GPU出現後,CPU的問題早已不像以前那麼突出,一些新的手段和技術也可以被應用進來了。
骨骼動畫相對于幀動畫而言,更加靈活多變,但同時,骨骼動畫需要更多的計算量,是以骨骼動畫往往應用在需要着重展現動作細節的模型中。
骨骼動畫技術後于幀動畫技術出現。
最開始,骨骼動畫僅用于非實時渲染的模組化領域,如3DMax這類模組化軟體之中,以友善美工的模組化。
後來,CPU從渲染中解放後,骨骼動畫才用于實時渲染的遊戲中。
骨骼動畫原理
骨骼動畫的想法來源于人體骨骼。
例如說,人的上肢所有肌肉和皮膚都受上肢骨胳的影響,而人的踝關節則分别承受小腿骨胳和腳骨的影響。
根據這個我們可以将骨骼動畫了解為兩個概念: 骨骼:用以控制蒙皮的一種抽象的概念,如人體骨骼控制皮膚。 蒙皮:被骨骼控制、并顯示在外的因素,如人體的皮膚被骨骼所影響。
骨骼動畫原理:蒙皮(SKINMESH)
在最終的渲染結果中,蒙皮将完全顯示出來,蒙皮實際上就是頂點、法線和紋理坐标等将被渲染的元素。
而其中,最關鍵的當然是頂點,頂點将直接被骨頭牽扯運動,因而使得整個模型呈現骨骼所決定的樣子。
骨骼動畫原理:骨骼(SKELETON)
骨骼是一種抽象的概念,在最終的渲染結果中,它不可見。
類如人體骨骼、骨骼是若幹骨頭(Bone)成樹狀的集合體,而每塊骨頭又分别與若幹數量的蒙皮頂點關聯。
當骨頭運動的時候,與之關聯的所有蒙皮頂點也會受骨骼的影響而運動。
骨頭與蒙皮頂點的關聯需要考慮到每塊骨頭對蒙皮頂點的影響。 盡管大部分情況下,一個頂點将僅僅被一個骨頭的影響,但是關節處的頂點往往被多根骨頭影響,例如踝關節,可能會分别受小腿骨50%和腳骨50%的影響,這種影響叫作權重(Weight)。
在這種情況下,我們稱踝關節的這些頂點,受小腿骨影響的權重是50%,受腳骨影響的權重也是50%。
而在cocos2dx中常用的骨骼動畫編輯器就是cocosStudio和spine了。
但是就我的感受,2款編輯器中spine可以說是完爆ccs的骨骼動畫部分,不僅因為spine容易上手,并且它提供了mesh,通常來說一張紋理隻有4個頂點,左下右下左上右上,但是有了mesh,我們可以抓取這個圖檔,并設定大量的頂點給它,之後就能通過對頂點的設定完成圖檔的拉伸形變,比如乳搖功能。
spine中30幀為1s,最新版中也提供了IK,可以将骨骼固定,做動作。
具體的編輯我就不說了,主要說一下運作庫。
首先提供spine的最新運作庫,當然這不是官方的,裡面封裝了ffd,是我自己修改過并添加了ffd代碼,采用newSkeletonAnimation差別原先的skeletonAnimation具體點選:spine FFD runtime
其中要注意的幾個地方:
1. 1.8版本以上的atlas做了修改 在第二行添加了size 是以隻要删除size這一行即可,
2. 動作制作的時候如果層級有發生改變或者切換了skin,slot,請K幀一下,修改的部分:draworder和bone,一般首尾都K一下就行了。
3.至于.skel的binary檔案,是spine本身就提供的二進制檔案,性能必然高于json,本人目前正在修改完善spine runtime的binary(因為官方也不提供,完成起來慢了點,以後或許會放出)。
4.newskeletonanimation在create時候最後一個參數scale别忘了。
說一下runtime中常用的一些方法:
skeletonNode = NEWSkeletonAnimation::createWithFile("spineboy.json", "spineboy.atlas", 0.6f);//建立動作,最後的參數為scale.
skeletonNode->startListener = [this] (int trackIndex) {//設定動作開始檢測的監聽器
spTrackEntry* entry = spAnimationState_getCurrent(skeletonNode->state, trackIndex);//獲得目前動作的狀态資訊
const char* animationName = (entry && entry->animation) ? entry->animation->name : 0;
log("%d start: %s", trackIndex, animationName);
};
skeletonNode->endListener = [] (int trackIndex) {//同理,結束動作監聽
log("%d end", trackIndex);
};
skeletonNode->completeListener = [] (int trackIndex, int loopCount) {//同理,完成動作監聽
log("%d complete: %d", trackIndex, loopCount);
};
skeletonNode->eventListener = [] (int trackIndex, spEvent* event) {//同理,事件監聽,在spine中編輯好event導出
log("%d event: %s, %d, %f, %s", trackIndex, event->data->name, event->intValue, event->floatValue, event->stringValue);
};
skeletonNode->setMix("walk", "jump", 0.2f);//協調動作
skeletonNode->setMix("jump", "run", 0.2f);
skeletonNode->setAnimation(0, "walk", true);//設定動作,可用多個set進行動作混合
spTrackEntry* jumpEntry = skeletonNode->addAnimation(0, "jump", false, 3);//添加動作,在上個動作直接玩之後,之後為循環參數和delay
skeletonNode->addAnimation(0, "run", true);
skeletonNode->setStartListener(jumpEntry, [] (int trackIndex) {
log("jumped!");
});</span>
skeletonNode->timeScale = 0.3f;//動作間隔
skeletonNode->debugBones = true;//debug模式
skeletonNode->update(0);//更新動作
spBone* root=skeletonNode->findBone("root");//找到name為root的骨骼,同理 通過 findslot 可以找到名字為name的slot
順便吐糟一下cocos2dx 官方,雖說cocosstduio是親兒子吧,但是也不能不管spine啊,spine的ffd一直愣是沒加上去,逼得我自己改過來- =!
spine執行個體 附上執行個體