2013/3/3

Spriter好好玩系列 (四) ---- 使用SpriterMC


說在前頭:
如果你看到上一篇的SpriterAS已經覺得很夠用的話,那這篇SpriterMC其實可以直接跳過。原因是我個人認為SpriterAS目前的實用性大過於SpriterMC,而寫這篇只是因為都花時間研究了,就留下點筆記而已。另外SpriterMC唯一較強的是目前有支援Spriter的Bones及IK,所以除非有此特別需求或是好奇想看看的話,個人建議可直接跳過。
(以上心得基於SpriterMC v1.3版的狀況)

.官網:http://www.sammyjoeosborne.com/SpriterMC/

.github載點:https://github.com/sammyjoeosborne/spritermc

.基本用法:http://wiki.starling-framework.org/extensions/spritermc#usage

先看一下範例:

(click to open)
Download Source

載入SCML:
在官網中是這樣寫的:
var monster1:SpriterMC = SpriterMCFactory.createSpriterMC("monster", "xml/monster.scml");
monster1.play(); //Note: SpriterMC's will not actually start playing or show up on stage until SpriterMC.SPRITER_MC_READY is broadcast
 
//Add each SpriterMC to a Juggler, just like a regular Starling MovieClip
myJuggler.add(monster1);
但他們另外提供了事先做好TextureAtlas的做法,並且推薦用這種:
   var monster1:SpriterMC = SpriterMCFactory.createSpriterMC("monster", "xml/monster.scml", _textureAtlas);
monster1.playbackSpeed = 1.5; //Demonstrating playbackSpeed, which is like Scale, 1 == 100%. You can also set negative values to play backward
monster1.play(); //Note: SpriterMC's will not actually start playing or show up on stage until SpriterMC.SPRITER_MC_READY is broadcast
 
//Add each SpriterMC to a Juggler, just like a regular Starling MovieClip
myJuggler.add(monster1);
但我個人測試結果是,你必需使用TextureAtlas的寫法,否則使用第一種的話,執行時會出現error。

如何使用TextureAtlas:
照SpriterMC官網的說明,在他解析並產生這些SCML動畫時,會動態將所有的元件圖檔拼成一張大張的TextrueAtlas,這樣更能符合Starling的圖形加速處理,所以他建議使用者不如一開始自己就先手動做好拼圖這個動作。這件事我們可以利用TexturePacker來做(格式選AS3/Starling)。做完會像這樣:

一張大拼圖及一個xml描述檔。而AS3寫法可參考以下:
protected function init():void{
   loadTexture('assets/heroTA.png') 
  }
  
  private function loadTexture(_url:String):void
  {
   var _loader:Loader = new Loader();
   _loader.contentLoaderInfo.addEventListener(starling.events.Event.COMPLETE, textureLoadedHandler);
   _loader.load(new URLRequest(_url));
  }
  
  private function textureLoadedHandler(e:*):void 
  {
   
   _heroTexture = Texture.fromBitmap(Bitmap(e.target.loader.content));
   loadTextureAtlasXML("assets/heroTA.xml");
  }
  
  private function loadTextureAtlasXML(_url:String):void 
  {
   var _urlLoader:URLLoader = new URLLoader(new URLRequest(_url));
   _urlLoader.addEventListener(starling.events.Event.COMPLETE, atlasXMLLoadedHandler);
  }
  
  private function atlasXMLLoadedHandler(e:*):void 
  {
   var _xml:XML = XML(e.target.data);
   _textureAtlas = new TextureAtlas(_heroTexture, _xml);
   createCharacters();
  }
  
  private function createCharacters():void
  { 
    _hero = SpriterMCFactory.createSpriterMC("heroIK", "assets/heroIK/heroIK.scml",_textureAtlas,spriterReadyHandler,true);
  }
  private function spriterReadyHandler(e:starling.events.Event):void 
  {
   _hero.x= 100
   _hero.y=380
   
   
   _hero.setAnimationByName('idle')
   _hero.play();
   
   addChild(_hero);
   Starling.juggler.add(_hero)
   
  }

頭尾相連的動畫
成功讀入scml後,首先會發現動畫有點問題,就是原本來Spriter裏,最後一個keyframe都會自動做tween連回第一個keyframe,除非你自己取消這個功能。但是從SpriterMC讀進來的動畫卻完全都沒有這個效果。

正常來說,我們在Spriter裏可以透過上圖a點來切換是否要開啟"頭尾相連"的功能,但在SpriterMC裏目前完全無效,所以變通的方法就是自行將0ms的keyframe複製到動畫最尾處,如上圖b點。

動畫播完的callBack
這個功能對遊戲開發十分重要,例如我們做完一個攻擊、做完一個跳躍動畫,都會需要馬上切換至一般狀態的動畫,所以動畫播完呼叫一個callback會是十分常做的事情。在SpriterAS裏有提供了addCallBack這樣的method,但SpriterMC目前尚未提供這樣的api,我只能先用變通的方式來處理。
   private function onClickJump(e:MouseEvent):void{
   if(_hero.currentAnimation.name == 'jump')return;
   
   _hero.setAnimationByName('jump')
   _hero.loop=false;
   _hero.play()
   _hero.addEventListener(starling.events.Event.ENTER_FRAME,onEnterCheck);
  
  }
  
  protected function onEnterCheck(e:starling.events.Event):void{
   if(_hero.isComplete){
    _hero.removeEventListener(starling.events.Event.ENTER_FRAME,onEnterCheck);
    _hero.setAnimationByName('idle')
    _hero.loop=true;
   }
  }
這邊我使用ENTER_FRAME不斷去check他的isComplete是否為true,如此才能知道他是不是播完了。而在使用isComplete時,有兩個注意事項:
1.需把loop設為false,如此isComplete才有機會變成true,否則會永遠得不到true。
2.我個人懷疑這是bug。當我設_hero.setAnimationByName('jump')並成功播完一次後,下次再執行_hero.play()時,isComplete會在一開始就是true了,也就是它並沒有因為重新play,而重設為false。為了解決這問題,我直接修改了SpriterMC裏Animation.as這支檔案的play函式:
internal function play():void
  {
   //if we aren't looping and play was called while the animation was on its last frame, restart it
   if (!_loop && _currentKeyIndex == _lastFrameIndex)
   {
    _currentTime = mainKeys[_firstFrameIndex].time; //this is gonna make currentTime 0 or the length of the animation, depending on which way the animation is playing
    _currentKeyIndex = _firstFrameIndex;
    
   }
   _animationEnded = false;//maso add
   _isPlaying = true;
  }
修改過後就能正常執行了。

其他心得
SpriterMC目前能做的應用很少,所以試到這裏就沒什麼好繼續了,所以其他請自行參閱官網。倒是有一點較特別的是,Spriter的timeline是以ms為單位,但SpriterMC裏卻提到了一個叫currentFrame屬性,這裏試了一下,所謂的frame其實是Spriter中的Keyframe,第一個Keyframe得到是0,第二個是1,以此類推。不過在沒有call back的應用下,這個currentFrame其實也沒太大的用處。



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

沒有留言: