サイトトップ

Director Flash 書籍 業務内容 プロフィール

Adobe Flash非公式テクニカルノート

Starlingフレームワークでインスタンスをドラッグ&ドロップする

ID: FN1204001 Product: Flash CS5 and above Platform: All Version: 11 and above/ActionScript 3.0

Starlingフレームワークでは、マウスイベントはDisplayObject.touchイベント(定数TouchEvent.TOUCH)で扱います。本稿は、DisplayObject.touchイベントを使ったインスタンスのドラッグ&ドロップのやり方についてご説明します。


01 Quadインスタンスをステージに置く
まずは、ドラッグするインスタンスをステージに置かなければなりません。矩形のQuadインスタンスをひとつつくることにします。Starlingフレームワークのルートクラス(MySprite)はつぎのように定めます。ステートメントの頭に添えた番号は、後に掲げるドラッグ&ドロップまでつくり込んだクラス定義のスクリプト001の行を示します。

    // ActionScript 3.0クラス定義ファイル: MySprite.as
  1. package {
  1.   import starling.display.Sprite;
  2.   import starling.display.Quad;
  3.   import starling.events.Event;
  1.   public class MySprite extends Sprite {
  2.     private var square:Quad;
  3.     private var nUnit:Number = 50;
  4.     public function MySprite() {
  5.       addEventListener(Event.ADDED_TO_STAGE, initialize);
  6.     }
  7.     private function initialize(eventObject:Event):void {
  8.       square = new Quad(nUnit, nUnit, 0x0000FF);
  9.       addChild(square);
  10.       square.x = nUnit;
  11.       square.y = nUnit;
  12.       square.pivotX = nUnit / 2;
  13.       square.pivotY = nUnit / 2;
  1.     }
  1.   }
  2. }

そして、クラス定義のASファイルと同じ場所に保存したFlashムービー(FLA)ファイルには、メインタイムラインにつぎのフレームアクションが書かれているものとします。[パブリッシュプレビュー]で確かめると、ステージに50ピクセル四方の青い矩形のQuadインスタンスが表れます(図001)。

// フレームアクション: メインタイムライン
import starling.core.Starling;
var myStarling:Starling = new Starling(MySprite, stage);
myStarling.start();

図001■ステージに矩形のQuadインスタンスを置く
図001

これらのスクリプトの内容およびパブリッシュ設定などのStarlingフレームワークを使うために必要な準備については、基本的に「Starlingフレームワークを使う」でご説明しています。詳しくは、このノートをお読みください。


02 ドラッグ&ドロップのために用いるイベントおよびプロパティとメソッド
マウスのインタラクティブな操作は、DisplayObject.touchイベントで扱われます。そして、どのような操作をマウスでしたのかは、DisplayObject.touchイベントのリスナーが引数に受取るTouchEventオブジェクトからTouchEvent.getTouch()メソッドによりTouchオブジェクトを得て、そのTouch.phaseプロパティで調べます[*1]

Touch.phaseプロパティがとる文字列の値は、TouchPhaseクラスに定数として定められています。Touchクラスという名前からもおわかりのように、マウスだけでなくタッチスクリーンのインタラクションも同じように扱えます。TouchPhaseクラスの定数とタッチスクリーンおよびマウスの操作は、次表001のとおりです。

表001■TouchPhaseクラスの定数とタッチスクリーンおよびマウスの操作
TouchPhaseクラスの定数 操作
タッチスクリーン マウス
BEGAN 画面に触れる マウスボタンを押す
ENDED 画面から指を離す マウスボタンを放す
HOVER マウスポインタを重ねる
MOVED 画面に触れた指を動かす ボタンは押したままマウスを動かす
STATIONARY 画面に触れたまま動かさない ボタンを押したままマウスは動かさない

マウスのドラッグに当たるTouch.phaseプロパティの値は、TouchPhase.MOVEDです。Flashに定義済みのInteractiveObject.mouseMoveイベント(定数MouseEvent.MOUSE_MOVE)と違って、インスタンスの上でマウスボタンは押したまま動かしていることを示します。ですから、この値だけでドラッグが捉えられ、別にマウスボタンを押したか放したかは調べずに済むのです。

さて、Touch.phaseプロパティを調べるには、Touchオブジェクトを得なければなりません。DisplayObject.touchイベントのリスナーが受取ったTouchEventオブジェクトからTouchオブジェクトを取出すのはTouchEvent.getTouch()メソッドです。引数はふたつあります。

TouchEventオブジェクト.getTouch(DisplayObjectオフジェクト, TouchPhaseクラス定数)

第1引数は、DisplayObject.touchイベントの受取りを調べる表示リストの頂点となるDisplayObjectインスタンスです。TouchEvent.getTouch()メソッドは、この引数に定めたインスタンスとその表示リスト下層の子インスタンスからTouchオブジェクトを探します。さらに第2引数のTouchPhaseクラス定数で、DisplayObject.touchイベントの操作を絞り込めます。デフォルト値はnullで、すべてのDisplayObject.touchイベントになります。ふたつの引数で定まるTouchオブジェクトが見つかればそのオブジェクト、なければnullが返ります。

もっとも、DisplayObject.touchイベントを扱うリスナーは、EventDispatcher.addEventListener()メソッドで決まります。それに、TouchEvent.getTouch()メソッドの第2引数を組合わせれば、第1引数を絞り込まなければならないことは多くありません。その場合、第1引数は表示リスト最上位のStageオブジェクトを渡します。

また、インスタンスをドラッグするためには、DisplayObject.touchイベントが起こったときのマウスポインタの座標を知りたいです。Touch.getLocation()メソッドは、その座標をPointオブジェクトで返します。引数のDisplayObjectインスタンスが、座標空間を定めます。

Touchオブジェクト.getLocation(DisplayObjectオフジェクト)

[*1] 併せて、akihiro kamijo「Starling のタッチイベントと Touch クラス」が参考になります。


03 インスタンスをドラッグ&ドロップするスクリプト
ステージに置いたQuadインスタンスをドラッグ&ドロップするStarlingルートクラス(MySprite)の定義は以下のスクリプト001のとおりです。まず、必要なクラスのimport宣言(第7〜9行目)をしたうえで、QuadインスタンスのDisplayObject.touchイベントにリスナーメソッド(dispatchMouseEvent())を加えます(第23行目)。

つぎに、リスナーメソッド(dispatchMouseEvent())はさまざまなマウス操作を同じDisplayObject.touchイベントとして受取るため、操作に応じたメソッドは別に定めることにして交通整理に徹しました。TouchEvent.getTouch()メソッドでドラッグ操作(定数TouchPhase.MOVED)のTouchオブジェクトを取出し(第33行目)、オブジェクトがあればドラッグ用のメソッドを呼出します(第34〜36行目)。

そして、ドラッグ用のメソッド(onMouseMove())は、マウス操作が加えられたインスタンスをEvent.currentTargetプロパティで取出し(第26行目)、またTouchEvent.getTouch()メソッドでTouchオブジェクト(第27行目)、Touch.getLocation()メソッドで操作が加わったマウス座標(第28行目)を得ています。そのうえで、ドラッグするインスタンスの位置を、マウス座標に合わせます(第29〜30行目)。

スクリプト001■Quadインスタンスをドラッグ&ドロップする
    // ActionScript 3.0クラス定義ファイル: MySprite.as
  1. package {
  2.   import flash.geom.Point;
  3.   import starling.display.DisplayObject;
  4.   import starling.display.Sprite;
  5.   import starling.display.Quad;
  6.   import starling.events.Event;
  7.   import starling.events.TouchEvent;
  8.   import starling.events.Touch;
  9.   import starling.events.TouchPhase;
  10.   public class MySprite extends Sprite {
  11.     private var square:Quad;
  12.     private var nUnit:Number = 50;
  13.     public function MySprite() {
  14.       addEventListener(Event.ADDED_TO_STAGE, initialize);
  15.     }
  16.     private function initialize(eventObject:Event):void {
  17.       square = new Quad(nUnit, nUnit, 0x0000FF);
  18.       addChild(square);
  19.       square.x = nUnit;
  20.       square.y = nUnit;
  21.       square.pivotX = nUnit / 2;
  22.       square.pivotY = nUnit / 2;
  23.       square.addEventListener(TouchEvent.TOUCH, dispatchMouseEvent);
  24.     }
  25.     private function onMouseMove(eventObject:TouchEvent):void {
  26.       var instance:DisplayObject = eventObject.currentTarget as DisplayObject;
  27.       var myTouch:Touch = eventObject.getTouch(stage);
  28.       var currentPoint:Point = myTouch.getLocation(parent);
  29.       instance.x = currentPoint.x;
  30.       instance.y = currentPoint.y;
  31.     }
  32.     private function dispatchMouseEvent(eventObject:TouchEvent):void {
  33.       var myTouch:Touch = eventObject.getTouch(stage, TouchPhase.MOVED);
  34.       if (myTouch) {
  35.         onMouseMove(eventObject);
  36.       }
  37.     }
  38.   }
  39. }

これでステージのQuadインスタンスがドラッグドロップできます。ただ、細かい動きでひとつ気になります。インスタンスの端でクリックしても、ドラッグし始めるとポインタがインスタンスの原点(基準点)にきてしまうことです(図002)。次項で、この問題を直しましょう。

図002■インスタンスの端でクリックしてもドラッグし始めるとポインタが真ん中にくる
図002左   図002右


04 マウスポインタが移動した座標を調べる
インスタンス上でドラッグし始めたマウスポインタの位置を保つということは、Flashコンテンツのドラッグ操作でよく行われます。マウスポインタとインスタンスの座標の差をオフセットとして覚えておき、ドラッグするインスタンスの位置をその分ずらすというのがお決まりです。

ところが、Touchオブジェクトを使うと、初めに座標の差を求める手間が省けます。それは、Touchオブジェクトそのものが、ひとつ前のDisplayObject.touchイベントのときのマウス座標を覚えているからです。その座標のPointオブジェクトが、Touch.getPreviousLocation()メソッドで得られます。座標がひとつ古いというだけで、使い方はTouch.getLocation()メソッドと同じです。

Touchオブジェクト.getPreviousLocation(DisplayObjectオフジェクト)

前掲スクリプト001で、手を加えるのはドラッグ用のメソッド(onMouseMove())です。でき上がりはスクリプト002として後に掲げました。その中からドラッグ用のメソッドを抜書きすると、つぎのとおりです(行番号は後掲スクリプト002にもとづきます)。

  1.     private function onMouseMove(eventObject:TouchEvent):void {
  2.       var instance:DisplayObject = eventObject.currentTarget as DisplayObject;
  3.       var myTouch:Touch = eventObject.getTouch(stage);
  4.       var currentPoint:Point = myTouch.getLocation(instance);
  5.       var lastPoint:Point = myTouch.getPreviousLocation(instance);
  6.       var movePoint:Point = currentPoint.subtract(lastPoint);
  7.       instance.x += movePoint.x;
  8.       instance.y += movePoint.y;
  9.     }

Touch.getPreviousLocation()メソッドでひとつ前のイベントのときのマウスポインタの座標を調べ(第29行目)、現在のポインタの座標との差をPoint.subtract()メソッドにより求めたうえで(第30行目)、Quadインスタンスの位置をその座標の変化に合わせて動かしています(第31〜32行目)。

さて、このご説明ですっきりおわかりになれば結構です。けれど、戸惑っている方も少なくないでしょう。というのは、前掲スクリプト001と、細かいところがあちこち変わっているからです。

スクリプト001はTouch.getLocation()メソッドの引数に親インスタンス(DisplayObject.parentプロパティ)を渡していました。ところが、スクリプト002では、Touch.getPreviousLocation()メソッドとともに、ドラッグされるインスタンス自身が引数になっています(第28〜29行目)。また、インスタンスへの座標の設定が代入演算子=(スクリプト001)でなく、加算後代入演算子+=(スクリプト002第31〜32行目)です。

それはスクリプト002が、前掲スクリプト001と座標の捉え方を変えたためです。スクリプト001は、マウスポインタの座標を調べて、インスタンスをその位置に動かしました。

けれど、スクリプト002は、マウスポインタの座標がどこにあるかは気にしません。ポインタがどれだけ動いたかの差を求め、インスタンスにマウスポインタと同じ動きをさせているだけなのです[*2]。けれど結果として、マウスポインタとインスタンスとの座標の差(ずれ)が保たれるのです(図003)。

スクリプト002■Quadインスタンスをマウスポインタとの位置関係は保ったままドラッグ&ドロップする
    // ActionScript 3.0クラス定義ファイル: MySprite.as
  1. package {
  2.   import flash.geom.Point;
  3.   import starling.display.DisplayObject;
  4.   import starling.display.Sprite;
  5.   import starling.display.Quad;
  6.   import starling.events.Event;
  7.   import starling.events.TouchEvent;
  8.   import starling.events.Touch;
  9.   import starling.events.TouchPhase;
  10.   public class MySprite extends Sprite {
  11.     private var square:Quad;
  12.     private var nUnit:Number = 50;
  13.     public function MySprite() {
  14.       addEventListener(Event.ADDED_TO_STAGE, initialize);
  15.     }
  16.     private function initialize(eventObject:Event):void {
  17.       square = new Quad(nUnit, nUnit, 0x0000FF);
  18.       addChild(square);
  19.       square.x = nUnit;
  20.       square.y = nUnit;
  21.       square.pivotX = nUnit / 2;
  22.       square.pivotY = nUnit / 2;
  23.       square.addEventListener(TouchEvent.TOUCH, dispatchMouseEvent);
  24.     }
  25.     private function onMouseMove(eventObject:TouchEvent):void {
  26.       var instance:DisplayObject = eventObject.currentTarget as DisplayObject;
  27.       var myTouch:Touch = eventObject.getTouch(stage);
  28.       var currentPoint:Point = myTouch.getLocation(instance);
  29.       var lastPoint:Point = myTouch.getPreviousLocation(instance);
  30.       var movePoint:Point = currentPoint.subtract(lastPoint);
  31.       instance.x += movePoint.x;
  32.       instance.y += movePoint.y;
  33.     }
  34.     private function dispatchMouseEvent(eventObject:TouchEvent):void {
  35.       var myTouch:Touch = eventObject.getTouch(stage, TouchPhase.MOVED);
  36.       if (myTouch) {
  37.         onMouseMove(eventObject);
  38.       }
  39.     }
  40.   }
  41. }

図003■初めにクリックしたポインタの位置は保たれたままインスタンスがドラッグできる
図003

[*2] したがって、Touch.getLocation()およびTouch.getPreviousLocation()メソッドに渡す引数のオブジェクトは、スクリプト001と同じく親インスタンスでも構いません。どちらのインスタンスから見ても、マウスポインタの座標の動きは変わらないからです。


作成者: 野中文雄
作成日: 2012年4月1日


Copyright © 2001-2012 Fumio Nonaka.  All rights reserved.