Laya 動畫系列三 骨骼動畫

一、骨骼動畫與幀動畫的比較與選擇

參考
幀動畫,Tween 動畫,骨骼動畫等動畫的關系及分類是怎樣的?
骨骼動畫在H5使用攻略
骨骼動畫原理與前端實現淺談
1.幀動畫
動畫的每一幀都獨立地保存在媒體內,連續播放這些幀即形成了連續動態。一般常見于GIF動畫。
2.Tween動畫
受到了Flash的影響。Tween其實是In-between的簡寫,指的是計算機自動插值補全關鍵幀Keyframe之間的動畫。補全的動畫既可以是動態Motion也可以是變形Morph。所以Tween其實只是一個補全的過程,更加合理的稱呼應該是關鍵幀動畫。
3.骨骼動畫
“骨骼動畫”并不是一種動畫制作形式,而是一種制作手段。目前的動畫物體,特別是有機體,其構成和變形非常復雜,比方說一個由成千上萬個點構成的3D角色。盡管動畫本質上是這些點在空間內做位移構成的,但直接驅動這些點非人力能為。因此我們把運動簡化成骨骼來代表,并把點一一映射到骨骼上。構成骨骼的過程叫Rigging,把點映射到骨骼上的過程叫蒙皮Skinning。
4.骨骼動畫比傳統的逐幀動畫要求更高的處理器性能(幀動畫吃內存),但同時它也具有更多的優勢:

  • 動畫更加生動逼真
  • 圖片資源占最小的存儲空間,骨骼動畫的圖片容量可以減少90%(配置文件H5的壓縮方案后面詳解)
  • 動畫切換自動補間。過渡動畫自動生成,讓動作更加靈動
  • 骨骼可控 :可以通過代碼控制骨骼,輕松實現角色裝備更換,甚至可對某骨骼做特殊控制或事件監聽骨骼事件幀,動畫執行到某個動作或某個幀,觸發自定義事件行為
  • 動作數據繼承,多角色可共用一套動畫數據
  • 可結合物理引擎和碰撞檢測

5.效率與選擇
參考想知道用laya做動畫和用spine做動畫然后導進laya 占用的資源是一樣大的嗎
如果項目中同屏動畫小于10-20個的話可以使用骨骼動畫(骨骼動畫節省圖片資源,運算量大
如果同屏的動畫數量大于20個以上,建議用序列幀animation,引擎針對animation做了極致的優化,在渲染提交的過程中一個圖集是一個drawcall

參考關于白鷺引擎中dragonbones制作的骨骼動畫的執行效率問題
Q:我游戲中不添加任何邏輯,只是播放20個小綠龍的行走動畫,在手機上效率就直接掉到20幀了,暫停動畫播放后,又會恢復到60幀。說明就是骨骼動畫的執行效率問題了,這樣的話,是不是就不能使用骨骼動畫來實現了?我的游戲場景里鐵定會超過30個,有可能會到60個骨骼動畫
A:這里可以給你一個表格, 是官方的數據
在小米2,上使用DB,建議同屏最多渲染圖片(骨骼)數

DB 普通模式 DB 極速模式
h5 80 150
打包app (未開啟db c++) 250 500
打包app (開啟db c++) 750 1500

如果你想做純h5的游戲,即使使用極速模式,建議同屏也不要超多150個骨骼。極速模式可以參考極速模式
小龍的角色有17個骨骼,也就是同屏不要超過9個。
基于你同屏30個角色的需求,至少要使用打包app的方案。

參考1個骨骼動畫,為何drawcall不是1,資源都在一個大圖上
如果用到網格和換膚的話,會導致drawCall的增加。這個是正常的,建議開發者減少2者的使用(可以將網格處的圖片轉成png進行使用)
參考骨骼動畫蒙皮和網格動畫性能問題
骨骼動畫本身性能就是比較耗的,尤其是用到蒙皮和網格,animation是目前最高效的動畫方式,建議開發者最好不要使用蒙皮和網格動畫,如果資源量不大的話,以圖集動畫為最優!
參考為啥不同的骨骼動畫drawCall不一樣主要受啥影響
主要應該是蒙皮和網格吧,還有骨骼數的不同吧,理論上骨骼數不要超過255,但是建議最好不要超過200

6.參考2D動畫,是用dragonbones還是spine更強大?
spine比drangonbones多的功能:IK,自由變形,多種輸出格式。如果只是做骨骼動畫,那龍骨是夠用的。如果對自由變形要求比較高,那就只能選spine了。 希望龍骨盡快迎頭趕上,可以完全取代spine。畢竟spine的價格高的離譜,而且希望國產軟件能不輸給國外軟件。

二、dragonbones使用

參考dragonbones官網
官網指南video

三、Laya中使用骨骼動畫

參考
Laya 播放Spine骨骼動畫
Laya 播放DragonBones動畫
Laya 骨骼動畫模板、播放模式、換裝、切換動作
Egret 機甲戰士

引擎中使用骨骼動畫無論是Spine還是DragonBone其實用法都是一樣的,因為在轉換過程中轉換工具將兩種動畫都轉成了引擎可以使用的相同的格式


image.png

1.直接加載使用

package
{
    import laya.ani.bone.Skeleton;
    public class DragonBonesDemo
    {
        public function DragonBonesDemo()
        {
            //初始化舞臺
            Laya.init(1334, 750);
            //創建一個Skeleton對象
            var skeleton:Skeleton = new Skeleton();
             //添加到舞臺
            Laya.stage.addChild(skeleton);
            skeleton.pos(600,350);
            //通過加載直接創建動畫
            skeleton.load("res/DragonBones/rooster/Rooster_Ani.sk");
        }
    }
}

也可以預加載:
參考多個骨骼動畫可以預加載嗎,有好幾十個

image.png

2.使用骨骼動畫模板
要更好的使用骨骼動畫就必須提到模板的概念,在LayaAir引擎中模板是一種特別的概念,表示一種數據結構,這種數據結構可以被復用。骨骼動畫就使用到了模板,對于同一個動畫來說,可以只創建一個動畫模板,然后實例多個播放的實例,這樣內存中就只有一份的動畫數據,但是卻可以在舞臺上顯示多個動畫

package  
{
    import laya.ani.bone.Skeleton;
    import laya.ani.bone.Templet;
    import laya.events.Event;
    import laya.webgl.WebGL;
    /**
     * ...
     * @author ww
     */
    public class SkeletonTempletSample 
    {
        public var templet:Templet;
        public function SkeletonTempletSample() 
        {
            WebGL.enable();
            Laya.init(1000, 900);
            //創建動畫模板
            templet = new Templet();
            templet.on(Event.COMPLETE, this, parseComplete);
            templet.on(Event.ERROR, this, onError);
            //加載動畫文件
            templet.loadAni("res/spine/goblins/goblins.sk");
        }
        private function onError():void
        {
            trace("parse error");
        }
        private function parseComplete():void
        {
            //創建第一個動畫
            var skeleton0:Skeleton;
            //從動畫模板創建動畫播放對象
            skeleton0 = templet.buildArmature(0);
            skeleton0.pos(200, 700);
            //切換動畫皮膚
            skeleton0.showSkinByIndex(1);
            //播放
            skeleton0.play(0,true);
            Laya.stage.addChild(skeleton0);
            //創建第二個動畫
            var skeleton1:Skeleton;
            skeleton1 = templet.buildArmature(0);
            skeleton1.pos(500, 700);
            skeleton1.showSkinByIndex(1);
            skeleton1.play(0,true);
            Laya.stage.addChild(skeleton1);
        }
    }
}

3.播放模式
我們在從模板創建動畫的時候傳了一個參數0,這個參數就表示動畫的播放模式。(skeleton0 = templet.buildArmature(0);)動畫有三個播放模式,下面分別說明:

  • 0:使用模板緩沖的數據,模板緩沖的數據,不允許修改 (內存開銷小,計算開銷小,不支持換裝)
  • 1:使用動畫自己的緩沖區,每個動畫都會有自己的緩沖區,相當耗費內存。(內存開銷大,計算開銷小,支持換裝)
  • 2:使用動態方式,去實時去畫(內存開銷小,計算開銷大,支持換裝,不建議使用)

? 這三種模式中 0:不支持換裝,1,2支持換裝。

4.換裝

//切換動畫皮膚
skeleton0.showSkinByIndex(1);

我們在這里傳了一個參數1,表示切換到1號皮膚。事實上這個動畫有三個皮膚,0號是默認皮膚,1號是男角色皮膚,2號是女角色皮膚,下面我們給一個顯示不同皮膚的例子。

//創建第三個動畫
var skeleton2:Skeleton;
skeleton2 = templet.buildArmature(0);
skeleton2.pos(700, 700);
//切換動畫皮膚 使用標號為2的皮膚
skeleton2.showSkinByIndex(2);
skeleton2.play(0,true);
Laya.stage.addChild(skeleton2);

在另外一個例子中,使用了showSkinByName

var mSkinList = ["goblin","goblingirl"];

function parseComplete() {
    //創建模式為1,可以啟用換裝
    mArmature = mFactory.buildArmature(1);
    mArmature.x = mStartX;
    mArmature.y = mStartY;
    Laya.stage.addChild(mArmature);
    mArmature.on(Event.STOPPED, this, completeHandler);
    play();
    changeSkin();
    Laya.timer.loop(1000, this, changeSkin);
}

function changeSkin()
{
    mCurrSkinIndex++;
    if (mCurrSkinIndex >= mSkinList.length)
    {
        mCurrSkinIndex = 0;
    }
    mArmature.showSkinByName(mSkinList[mCurrSkinIndex]);
}

在論壇網友分享的例子中,參考分享:Dragonbones/Spine的換膚操作
目前LayaAir下支持龍骨的局部換膚(根據插槽索引換膚、根據插槽name換膚、紋理換膚、網格換膚)、全局換膚。需注意:

  • Dragonbones不支持全局換膚,Spine支持全局換膚
  • 使用到IK和網格的動畫需要開啟WebGL,否則可能會出現皮膚丟失的情況
  • Dragonbones與spine的接口是一樣的,除第一種情況外,下面的示例通用于Dragonbones和spine

5.切換動作

private var skeleton:Skeleton;
private var text:Text;
private function test():void
{  
    skeleton = new Skeleton();
    skeleton.url = "res/spine/alien/alien.sk";
    skeleton.pos(300, 700);
    Laya.stage.addChild(skeleton);
    text = new Text();
    Laya.stage.addChild(text);
    text.color = "#00ff00";
    text.fontSize = 30;
    Laya.stage.addChild(text);
    Laya.stage.on(Event.MOUSE_DOWN, this, changeAction);
}
private var tActionID:int=0;
private function changeAction():void
{
    tActionID++;
    var aniCount:int;
    //獲取動畫動作數量
    aniCount = skeleton.getAnimNum();
    tActionID = tActionID % aniCount;
    //顯示當前要播放的動畫名
    text.text = skeleton.getAniNameByIndex(tActionID);
    //切換播放的動畫
    skeleton.play(tActionID, true);
}

6.事件
參考骨骼動畫--Spine事件

private parseComplete():void {
    //創建模式為1,可以啟用換裝
    this.mArmature = this.mFactory.buildArmature(1);
    this.mArmature.x = this.mStartX;
    this.mArmature.y = this.mStartY;
    this.mArmature.scale(0.5, 0.5);
    Laya.stage.addChild(this.mArmature);
    this.mArmature.on(Event.LABEL, this, this.onEvent);
    this.mArmature.on(Event.STOPPED, this, this.completeHandler);
    this.play();
}

private completeHandler():void
{
    this.play();
}

private play():void
{
    this.mCurrIndex++;
    if (this.mCurrIndex >= this.mArmature.getAnimNum())
    {
        this.mCurrIndex = 0;
    }
    this.mArmature.play(this.mCurrIndex,false);
    
}

private onEvent(e):void
{
    var tEventData:EventData = e as EventData;
    
    Laya.stage.addChild(this.mLabelSprite);
    this.mLabelSprite.x = this.mStartX;
    this.mLabelSprite.y = this.mStartY;
    this.mLabelSprite.graphics.clear();
    this.mLabelSprite.graphics.fillText(tEventData.name,
    0, 0, "20px Arial", "#ff0000", "center");
    Tween.to(this.mLabelSprite, { y:this.mStartY - 200 },
    1000, null,Handler.create(this,this.playEnd))
}

private playEnd():void
{
    this.mLabelSprite.removeSelf();
}

關于如何為動畫關鍵幀添加事件,參考分享:Skeleton下Event.LABLE('label')事件的使用

image.png

打開Dragonbones(或spine),選擇龍骨動畫的關鍵幀,在屬性面板會看到事件,單擊事件后面的文本框,添加自定義幀事件,命名隨意,譬如label、'label'、jump、walk....,支持多個幀事件

image.png

console.log("label event:",e)

參考分享:Skeleton如何監聽播放完成事件!
當skeleton.play(0,true)第二個參數為true時,每播放完一遍龍骨動畫,會自動觸發Event.COMPLET事件
skeleton.player.on(Event.COMPLETE,this,onComplete);
當skeleton.play(0,false)第二個參數為false時,當前動畫播放完成后,會自動觸發Event.STOPED事件,而不是Event.COMPLETE事件
skeleton.on(Event.STOPPED, this, completeHandler);

7.子骨骼動畫
參考關于骨骼動畫
Q:現在游戲中需要給人物加上翅膀,并且翅膀是可以更換的,請問下面的做法是否可以實現:

  • 將翅膀單獨做成一個骨骼動畫
  • 再根據需要將翅膀的骨骼動畫綁定到人物骨骼動畫上去(即翅膀骨骼動畫作為人物骨骼動畫的子骨骼動畫存在)
  • 換槽位的貼圖的方法我知道怎么做,但是翅膀根據不同的類型可能會有不同的動畫,所以想以子骨骼動畫的形式進行更換

請問上述方式是否可以實現
A:layaAir目前不支持翅膀骨骼動畫作為人物骨骼動畫的子骨骼動畫存在(骨骼動畫嵌套或者多骨骼播放不支持),建議開發者換種方式實現吧(可以對整體動畫的動作---翅膀+軀體進行調整,單個部位換膚)!

8.參考可以在動畫骨骼中插入Laya元件嗎
Q:在運行骨骼動畫時可以通過函數得到相應的骨骼或者插槽,那我可以通過骨骼或者插槽加入比如Sprite這些Laya元件進入動畫中嗎?比如我做了一系列的玩家移動、攻擊動畫,我想在人物的雙手位置加入一些粒子特效,并且是隨著人物狀態實時改變的,也就是動態添加進去的,可以實現嗎?雖然可以通過骨骼的世界坐標+旋轉縮放計算來達到在手的位置顯示對象的效果,但是無法插入到人物的層級中去,例如效果應該顯示在內部手和身體之間那一層,但是動畫外的對象無法插入到動畫內對象的層級中去。
A:你好,目前除了換膚外,不支持將粒子等其他元件插入到龍骨的插槽里

9.參考骨骼動畫加遮罩

10.參考分享:銷毀龍骨動畫!

public function startFun():void
{
    mAniPath = "Dragon/Dragon.sk";
    mFactory = new Templet();
    mFactory.on(Event.COMPLETE, this, parseComplete);
    mFactory.loadAni(mAniPath);
}

private function parseComplete(fac:Templet):void {
    //創建模式為1,可以啟用換裝
    mArmature = mFactory.buildArmature(0);
    mArmature.x = 400;
    mArmature.y = 500;
    mArmature.scale(0.5, 0.5);
    Laya.stage.addChild(mArmature);
    mArmature.on(Event.STOPPED, this, completeHandler);
    play();
}
        
private function completeHandler():void
{
    play();
}

private function play():void
{
    mCurrIndex++;
    if (mCurrIndex >= mArmature.getAnimNum())
    {
        mCurrIndex = 0;
    }
    mArmature.play(mCurrIndex,false);
}
        
public function destroy():void
{
    mArmature.stop();//停止龍骨動畫播放
    removeEvent();//移除事件
    mArmature.removeSelf();//從顯示列表移除龍骨動畫本身
    mArmature.removeChildren();//從顯示列表移除龍骨動畫子對象
    mArmature.destroy(true);//從顯存銷毀龍骨動畫及其子對象
    mFactory.destroy();//釋放動畫模板類下的紋理數據
    mFactory.releaseResource(true);//釋放龍骨資源
}

public function removeEvent():void
{
    mFactory.off(Event.COMPLETE, this, parseComplete);
    mArmature.off(Event.STOPPED, this, completeHandler);
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,637評論 25 708
  • 111. [動畫系統]如何將其他類型的動畫轉換成關鍵幀動畫? 動畫->點緩存->關鍵幀 112. [動畫]Unit...
    胤醚貔貅閱讀 13,112評論 3 90
  • 今天吼了也叫了,過后我在鏡子面前端詳了一陣自己發怒的樣子,眉頭緊皺,額頭上也長了一浪浪的橫紋,眼睛冒著兇光,真的不...
    遠遠_d1f1閱讀 129評論 0 3
  • 劉美芹種子日記 2017年 08 月 29日 [拳頭]身修家和 美麗中國 種子踐行日記 地方:南通家...
    劉小兔子閱讀 197評論 0 0
  • 通過一天的跟進挑戰了不起項目,對于這個活動有幾個點收獲比較大, 一.是對于選擇小區很重要, 二.是團隊的領隊要起到...
    愛思哲閱讀 545評論 0 0