2011年11月15日 星期二

一起來玩鳥 Starling Framework(5)Multi-Touch

這篇來談談Starling的Multi-Touch。前一篇也提到,Multi-Touch一樣是監聽TouchEvent.TOUCH,然後由TouchEvent的e.getTouches()取回多點的資訊。通常提到Multi-Touch會想到Gesture,不過Starling目前沒有GestureEvent可用。需要的時候只能自己動手寫。

在開始練習之前,我們要回到Document Class,Main.as。要使用Multi-Touch,要先設定Starling Class的靜態屬性:
Starling.multitouchEnabled = true;
然後Starling也提供了可以在電腦上用滑鼠模擬Multi-Touch的功能,這個功能則要在我們產生Starling實體之後,設定:
_starling.simulateMultitouch = true;

設定好之後,我們來做個Multi-Touch,以及自訂Gesture的功能。我們在場景上放一個Image,然後為這個Image加上兩指縮放、旋轉、平移等功能。先把程式碼列出來:
public class Game5 extends Sprite 
{
  private var _container:Sprite;
  private var _image:Image;
  private var _msgText:TextField;
  [Embed(source = "/assets/head.png")]//embed一張圖給Image當Texture用
  private static const HeadBitmap:Class;

  public function Game5() 
  {
    super();
    addEventListener(Event.ADDED_TO_STAGE, init);
  }

  private function init(e:Event):void
  {
    removeEventListener(Event.ADDED_TO_STAGE, init);

    _container = new Sprite();
    addChild(_container);
    addChild(new Stats());

    _image = new Image(Texture.fromBitmap(new HeadBitmap()));//新增一個Image,貼上Texture
    _image.x = (stage.stageWidth - _image.width) >> 1;//設定_image座標
    _image.y = (stage.stageHeight - _image.height) >> 1;//設定_image座標
    _container.addChild(_image);//將_image加到場景上

    _msgText = new TextField(300, 20, "", "Arial", 14, 0xFF0000, true);//新增一個Texture來顯示訊息
    _msgText.x = 100;
    _msgText.y = 80;
    _msgText.hAlign = HAlign.LEFT;
    _container.addChild(_msgText);

    _image.addEventListener(TouchEvent.TOUCH, onTouch);//_image加上監聽TouchEvent.TOUCH
  }

  private function onTouch(e:TouchEvent):void
  {
    var touches:Vector.<Touch> = e.getTouches(this);//取出多個touches的資訊
    var target:Image = Image(e.target);//取得發出事件的target,在這裡只有_image

    if (touches.length == 1)//如果只有一點touch
    {
      var touch:Touch = touches[0];//取得唯一一點touch
      if (touch.phase == TouchPhase.MOVED)//當這點在移動時
      {
        var currentPos:Point = touch.getLocation(target.parent);//取得這個touch的現在座標
        var previousPos:Point = touch.getPreviousLocation(target.parent);//取得這個touch的上一次座標
        target.x += currentPos.x - previousPos.x;//_image的x移動量為現在x座標減上次x座標
        target.y += currentPos.y - previousPos.y;//_image的y移動量為現在y座標減上次y座標
      }
    }

    if (touches.length == 2)//當touch有兩點時
    {
      var touchA:Touch = touches[0];//touchA為第一點
      var touchB:Touch = touches[1];//touchB為第二點

      var currentPosA:Point = touchA.getLocation(target.parent);//touchA現在座標
      var previousPosA:Point = touchA.getPreviousLocation(target.parent);//touchA上次座標
      var currentPosB:Point = touchB.getLocation(target.parent);//touchB現在座標
      var previousPosB:Point = touchB.getPreviousLocation(target.parent);//touchB上次座標

      var currentVector:Point = currentPosA.subtract(currentPosB);//現在兩點間的向量
      var previousVector:Point = previousPosA.subtract(previousPosB);//上一次兩點間的向量

      var currentAngle:Number = Math.atan2(currentVector.y, currentVector.x);//由現在向量算出現在弧度
      var previousAngle:Number = Math.atan2(previousVector.y, previousVector.x);//算出上一次弧度
      var deltaAngle:Number = currentAngle-previousAngle;//算出弧度差

      //以上一次兩點座標更新pivot
      var previousLocalA:Point  = touchA.getPreviousLocation(target);//touchA的上一次座標(target自己的座標系)
      var previousLocalB:Point  = touchB.getPreviousLocation(target);//touchB的上一次座標(target自己的座標系)
      target.pivotX = (previousLocalA.x + previousLocalB.x) * 0.5;//以上次兩點中心為新的pivot
      target.pivotY = (previousLocalA.y + previousLocalB.y) * 0.5;//以上次兩點中心為新的pivot

      //以現在兩點座標更新target座標
      target.x = (currentPosA.x + currentPosB.x) * 0.5;//以現在兩點算出新座標
      target.y = (currentPosA.y + currentPosB.y) * 0.5;//以現在兩點算出新座標

      //旋轉
      target.rotation += deltaAngle;//加上剛剛算出的旋轉量

      //縮放
      var sizeDiff:Number = currentVector.length / previousVector.length;//以前後兩的的向量長度比例算出縮放倍數
      target.scaleX *= sizeDiff;//乘上縮放倍數
      target.scaleY *= sizeDiff;//乘上縮放倍數
    }

    _msgText.text = "Touches number: " + touches.length;//顯示現在touch點數
  }
}

我們可以以touch.getLocation()以及touch.getPreviousLocation()來取得這次與上次的座標,輸入的參數是用來當作標基準的DisplayObject。當只有單一一點touch,而且在移動時,則平移target(_image)。當兩點時,使用兩點的前後座標,算出新的pivot中心、旋轉、縮放、新座標等。詳細的算法請參考程式碼與註解。要注意算pivot時我們是以target(_image)當座標系,因為pivot本來就是以target座標系來算;其他部分則以target.parent為座標系,我們一般得到某個DisplayObject的x與y座標也是以那個DisplayObject的parent座標系來算。

要使用模擬Multi-Touch,按住Ctrl可以看到兩個圓圈圖示,就代表兩個touch,移動滑鼠可以讓兩點往反方向移動。Ctrl+Shift可以讓兩點一起移動。Demo如下:
點我或圖看Demo

上一篇:一起來玩鳥 Starling Framework(4)TouchEvent,Touch,以及TouchPhase
下一篇:一起來玩鳥 Starling Framework(6)Juggler、Tween、以及DelayCall

2 則留言: