2013/3/2

Spriter好好玩系列 (三) ---- 使用SpriterAS


這篇要來把動畫跟AS3串接起來。用SpriterAS+Starling來做。

SpriterAS是一款基於Starling架構,專門播放Spriter動畫的AS3 Library。由加拿大的TreeFortress小組所開發。(TreeFortress專注於Mobile Game開發,成員包括了知名的gskinner大神)

.先看一下範例:

(click to open)
Download Source

上述範例我在Spriter裏做了三則animation,分別是idle(平常狀況)、fight_0(輕拳)、fight_1(重拳)。其他如眨眼、拳頭上的特效、頭掉下來,都是由程式控制的,而SpriterAS也支援Playback Speed的控制,這也是突顯了元件動畫優於連續圖檔的一大強項。

TreeFortress有寫了一篇Introducting SpriterAS - Play Spriter Animations with Starling。算是SpriterAS的入門介紹文,本篇範例也是看完他們的介紹文後的產物。建議有空可以先去看看,以下我只挑幾個重點介紹。

.github載點 https://github.com/treefortress/SpriterAS

.載入scml並叫出動畫
//**注意:SpriterAS是建立在Starling之上的,必需在Starling內
//**使用下列程式才能有作用。

protected var spLoader:SpriterLoader;

protected function init():void{
   
 spLoader = new SpriterLoader();
 spLoader.completed.add(onSpriterLoadComplete);
 //SpriterLoader可一次載入多個scml檔,所以參數是填一個array,可代入多個scml位置
 spLoader.load(['assets/hero/hero.scml'],1)
  
   
}
protected function onSpriterLoadComplete(spLoader:SpriterLoader):void
  {
   //從loader中取得動畫,以scml名稱為索引
   hero = spLoader.getSpriterClip('hero')
   //idle是hero.scml裏其中一段animation的名稱
   hero.play("idle")
   hero.x = 100
   hero.y =300
   addChild(hero)
   Starling.juggler.add(hero) 
}
這裏需要注意一點是,spLoader必需是class內的固定成員,不可以是function中的區域變數,例如我試過以下這樣寫的話,onSpriterLoadComplete將無法被觸發:
protected function init():void{
   
 var spLoader:SpriterLoader = new SpriterLoader();//不能用區域宣告
 spLoader.completed.add(onSpriterLoadComplete);
 spLoader.load(['assets/hero/hero.scml'],1)
  
   
}


.按下button切換animation至"fight_0"(輕拳),並在播放完畢時,切回"idle"
private function onClickFight0(e:MouseEvent):void
  {
   hero.play("fight_0")
   hero.addCallback(onOverFight,400)//400ms後呼叫onOverFight
}
protected function onOverFight():void{
 if(hero.animation.name != 'idle')hero.play("idle")
} 
直接使用 play("animation名稱"),即可切換動畫。
而 addCallback則是一個相當實用的method,可設定動畫播到多少ms時回call某個function,而此ms數會依照當初在Spriter裏設定的timeline來執行,因此不會受playback speed而受影響。
完整使用方式是:
addCallback(回call的function, 回call時間, 是否只執行一次)

.眨眼功能(動態切換元件)
private function onClickBlink(e:MouseEvent):void
  {
   hero.swapPiece("head_0", "head_1");
   TweenLite.delayedCall(.1,hero.unswapPiece,['head_0'])
}
swapPiece("A", "B")可將動畫中的A圖換成B圖。
unswapPiece("A")則是取消加諸在"A"身上的任何swapPiece效果。

.附著在拳頭上的粒子效果:
private function getHandPos():Point{
  var handImg:Image //Starling中的Image
  handImg = hero.getImage("leftHand_0");
  return new Point(handImg.x,handImg.y)
}
粒子效果非本篇重點,所以特效的部份就不多寫,上述程式只針對取得拳頭位置做示範,不過這邊要注意,取得的handImg.x及y後,記得還要加上hero.x及y,才是拳頭在畫面上看到的位置。

.頭掉下來(動態將元件拆離scml設定,或再組合回去)
private function onClickDropHead(e:MouseEvent):void
  {
   var headImg:Image = hero.getImage("head_0")
   if(headImg.y>-100){//弱弱的用y的位置來判斷目前頭的狀況
    hero.includePiece(headImg);//組回
    return;
   }
   
   hero.excludePiece(headImg,true)//脫離
   
   TweenLite.to(headImg,.6,{y:25,ease:Bounce.easeOut})
  }

.playback speed 控制
private function onSlideFPS(e:Event):void
  {
   _stage.frameRate = _fpsSlider.value
  } 
  private function onSlideSpeed(e:Event):void
  {
   hero.playbackSpeed = _speedSlider.value
  } 
這邊就沒什麼好解說的了,playbackSpeed代1的話表示正常進度,2為兩倍,0.5為半速,以此類推。

.動態才用到的元件
這邊算是個人使用心得。當我在製作範例中的眨眼功能,遇到了點小問題。原本我在Spriter裏編輯時只有三段animations,分別是idle, fight_0, fight_1,而這三段動畫完全沒用到眨眼的那張圖(head_1.png),使得當我在用hero.swapPiece("head_0", "head_1")時,會完全沒有做用。原因是當我查看scml檔的內容,發現scml在最前頭會先將所有圖檔列一份清單,之後的keyframe資訊就只會記下清單中的id,而這時又發現scml內只會記錄所有animation有用到過的圖檔,而不是所有放在project folder中的圖檔,所以它並沒有將我的head_1.png存進去,導致讓swapPiece("head_0", "head_1")無效。
變通的辨法是,再開一個新的animation,把所有程式會用到的圖都放進去,再輸出成scml即可。

例如上圖我就是把程式才會用到的圖,都放在"assets"這段animation裏。

.心得與注意事項:
SpriterAS使用下來,我個人覺得功能算是很完整了,已能符合大多數需求,唯一缺點是Spriter中的Bones功能尚未支援(如果有帶bone的scml,在spriterAS裏會爛掉),SpriterAS在官方blog是說未來會支援。不過我個人心得是,這部份還好,在我們真的在調比較細緻的動畫時,bones及IK其實沒什麼太大的幫助。另外作者群還有提到未來可能會推出JS版,有用JS的人可以期待一下。

*另一個library: SpriterMC有支援bones及IK,我在下一篇會簡單介紹一下SpriterMC



Index:
Spriter好好玩系列 (一) ---- 什麼是Spriter ?
Spriter好好玩系列 (二) ---- Spriter 基本操作
Spriter好好玩系列 (三) ---- 使用SpriterAS
Spriter好好玩系列 (四) ---- 使用SpriterMC

沒有留言: