FlashPlayer10.1の新機能、マルチタッチを管理する。
FlashPlayer10.1には、マルチタッチに対応したコンテンツに対応するために、新しいAPIが用意されています。
基本的には、Multitouch.inputModeを定義し、flash.events.TouchEventやflash.events.TransformGestureEventなどで値を受け取ることになります。
TransformGestureEventについては、すでにAdobe AIRデベロッパーセンターに掲載されている
「マルチタッチ機能を活用した AIR 2.0 アプリケーション Touch Viewer の紹介 | デベロッパーセンター」
が非常にわかりやすいです。
今回はTouchEventで効率的にタッチポイントを管理する方法について書きたいと思います。
TouchEventのタイプは
TOUCH_BEGIN
TOUCH_END
TOUCH_MOVE
TOUCH_OUT
TOUCH_OVER
TOUCH_ROLL_OUT
TOUCH_ROLL_OVER
TOUCH_TAP
以上の8つで、TouchEventではタッチ関連のプロパティが取得できます。タッチを管理していく上でメインになってくるのは touchPointID, localX, localYです。touchPointIDには、それぞれのタッチポイントにユニークなIDが割り振られており、localX, localYは、文字通りタッチポイントの座標です。
今回は、TOUCH_BEGIN, TOUCH_END, TOUCH_MOVEの3つにリスナを登録しておき、touchPointIDを添字としたPoint型のVectorにlocalX,localYを格納していく、という方法です。
実際には以下のようなコードになります。
public function MultiTouchSprite() { Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT; _startPoint = new Vector.<Point>(10); _currentPoint = new Vector.<Point>(10); _previewPoint = new Vector.<Point>(10); for (var i:int = 0; i < 10; i++) { _startPoint[i] = new Point(); _currentPoint[i] = new Point(); _previewPoint[i] = new Point(); } addEventListener(TouchEvent.TOUCH_BEGIN, _touchBeginHandler); addEventListener(TouchEvent.TOUCH_MOVE, _touchMoveHandler); addEventListener(TouchEvent.TOUCH_END, _touchEndHandler); } private function _touchBeginHandler(e:TouchEvent):void { _startPoint[e.touchPointID].x = e.localX; _startPoint[e.touchPointID].y = e.localY; }
今回はstartPoint, currentPoint, previewPointとい3つのVectorを用意しました。これでcurrentPoint[touchPointID].x, currentPoint[touchPointID].yとかすると、座標が取れます。
あと、僕の環境だと、touchPointIDが2から始まるんですけど、これってなんなんですかね。
ほかの環境でもそうでしょうか。
現在のタッチ個数を得るために、touchPointというint型を用意しておき、TOUCH_BEGINで++, TOUCH_ENDで–しています。
private function _touchBeginHandler(e:TouchEvent):void { _touchPoints++; } private function _touchEndHandler(e:TouchEvent):void { _touchPoints--; }
また、今回はタップ、ダブルタップを使えるようにしました。
TouchEvent.TOUCH_TAPを使うと、TOUCH_BEGIN, TOUCH_END, TOUCH_MOVE, TOUCH_TAPと4つのイベントすべてが発生してしまうので、それを回避するためです。
その部分のコードはこんな感じになります。
private function _touchBeginHandler(e:TouchEvent):void { _touchDownTime = getTimer(); } private function _touchEndHandler(e:TouchEvent):void { _touchPoints--; if (_touchPoints == 0) _touching = false; if (getTimer() - _touchDownTime < TIME_DIFF) { var dist:Number = Math.sqrt(Math.pow(_currentPoint[e.touchPointID].x - _startPoint[e.touchPointID].x, 2) + Math.pow(_currentPoint[e.touchPointID].y - _startPoint[e.touchPointID].y, 2)); if (Math.abs(dist) < POS_DIFF) _touchTap(e); } } public function _touchTap(e:TouchEvent):void { _tapCnt++; if (!_timer.running) { _timer.addEventListener(TimerEvent.TIMER_COMPLETE, _timerHandler); _timer.start(); } } private function _timerHandler(e:TimerEvent):void { switch(_tapCnt) { case 1 : atSingleTap.call(); break; case 2 : atDoubleTap.call(); break; case 3 : atTripleTap.call(); break; default:break; } _tapCnt = 0; }
- TOUCH_BEGINが発生してからTOUCH_ENDが発生するまでの時間がしきい値以下
- TOUCH_BEGINの座標とTOUCH_ENDの座標がしきい値以下
2つの条件を満たした時にTimer.start()させます。タイマー作動中にもう一度2つの条件を満たした場合はtapCountを増やし、timerイベントがディスパッチされたタイミングでtapCountを判定し、シングルタップ、ダブルタップを判断します。この処理を利用すれば、3回や4回など、それ以上の回数も取れます。
ネックはラグが発生することですかねー。
また、イベントのハンドリングはProgressionのCommandやAS2と同じように関数を与えておき、適切な場所でそいつを実行してやることにしました。
MultiTouchSprite.atTouchBegin = function():void { trace(currentPoint[touchObj.touchPointID]); };
最初はExTouchEventというのを作ってディスパッチしていたんですけど、Flash Pratform Campで、Mike Chambersがディスパッチはパフォーマンス低下するよ!って言ってたので変更しました。
また当初Array型を使ってPointを管理していたのをVector型に変更し、パフォーマンス向上を図っています。
MultiTouchSpriteというクラスとしてまとめておいたので、使ってもらえるとうれしいです。
http://alterna.in/demo/MultiTouchSprite/MultiTouchSprite.zip
[...] また、一つ前のエントリにて、少し解説をしています。 http://blog.alterna.in/2010/04/18-230815.php [...]