ActionScript 3.0で始めるオブジェクト指向スクリプティング

第62回Starlingフレームワークで動的に置いたビットマップをドラッグする

今回は、Starlingフレームワークに[ライブラリ]のビットマップからつくったインスタンスを置き、マウスでドラッグしてみる。どちらも少し定義済みActionScript 3.0の場合とは勝手が違う。ふたつに分けて進めていこう。

Starlingフレームワークに[ライブラリ]のビットマップを表示する

まずは準備として、⁠ライブラリ]のビットマップにクラス(Pen)を設定しておく図1⁠。そして、インスタンスをFlashのタイムラインに動的に置くスクリプトから、先に確かめておこう。すでに第34回「3次元空間における回転」ビットマップのインスタンスを動的に配置するで説明した処理のおさらいだ。

図1 ⁠ライブラリ]のビットマップにクラスを設定する
図1 [ライブラリ]のビットマップにクラスを設定する 図1 [ライブラリ]のビットマップにクラスを設定する

[ライブラリ]のビットマップ(クラスPen)は、ActionScript 3.0ではBitmapDataのサブクラスになる。そして、BitmapDataインスタンス(myBitmapData)はそのままタイムラインには置けない。そのため、Bitmapクラスのコンストラクタメソッドに引数として渡したうえで、Bitmapインスタンス(instance)DisplayObjectContainer.addChild()メソッドでタイムラインに加えるのだった。

var myBitmapData:BitmapData = new Pen();
var instance:Bitmap = new Bitmap(myBitmapData);
addChild(instance);

Starlingフレームワークではどうなるか。第1に、starling.display.DisplayObjectのサブクラスでなければ、Starlingのステージ(Stage3D)には置けない。そこで、Bitmapクラスに替えてStarlingのImageクラスを使う。コンストラクタメソッドの引数は、やはりビットマップイメージのオブジェクトだ。ただし第2に、BitmapDataオブジェクトは引数に使えない。Image()コンストラクタには、Textureクラスのオブジェクトを渡さなければならない表1⁠。

表1 ビットマップイメージをステージに表示するために用いるクラス
役割クラス
ActionScript 3.0定義済みStarlingフレームワーク
ビットマップイメージをもつflash.display.BitmapDatastarling.textures.Texture
ビットマップをステージに表示するflash.display.Bitmapstarling.display.Image

だが、使いたいのは[ライブラリ]のビットマップつまりBitmapDataインスタンスだ。この場合には、静的メソッドTexture.fromBitmapData()を用いればよい。引数のBitmapDataオブジェクトをTextureオブジェクトに変換して返してくれる。⁠ライブラリ]のビットマップをTextureオブジェクトに換え、Imageインスタンスに入れてStarlingフレームワークのステージに置くクラス定義(MySprite)がつぎのスクリプト1だ。

スクリプト1 Starlingフレームワークに[ライブラリ]のビットマップを置く
// ActionScript 3.0クラス定義ファイル: MySprite.as
package  {
  import flash.display.BitmapData;
  import starling.display.Sprite;
  import starling.display.Image;
  import starling.textures.Texture;
  import starling.events.Event;
  public class MySprite extends Sprite{
    private var instance:Image;
    public function MySprite() {
      addEventListener(Event.ADDED_TO_STAGE, initialize);
    }
    private function initialize(eventObject:Event):void {
      var myBitmapData:BitmapData = new Pen();
      var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
      instance = new Image(myTexture);
      addChild(instance);
      instance.x = stage.stageWidth / 2;
      instance.y = stage.stageHeight / 2;
      instance.pivotX = instance.width / 2;
      instance.pivotY = instance.height / 2;
    }
  }
}

これで[ライブラリ]に納めたビットマップのイメージが、ステージ中央に表示される図1⁠。前掲スクリプト1は、Imageインスタンスをつくるまでの処理を除けば、前回の「StarlingフレームワークでStage3Dを試す」で定義したクラスと基本的に変わらない。もちろん、前回述べたとおり、FLAファイルにはつぎのフレームアクションが書かれていて、⁠パブリッシュ設定]も正しく行われているものとする。

// フレームアクション: メインタイムライン
import starling.core.Starling;
var myStarling:Starling = new Starling(MySprite, stage);
myStarling.start();
図2 ビットマップのイメージがステージ中央に表示される
図2 ビットマップのイメージがステージ中央に表示される

定義済みActionScript 3.0のフレームアクションによるドラッグ

ふたつ目の課題はマウスドラッグだ。これも、定義済みActionScript 3.0のフレームアクションによるスクリプティングを先に確かめておこう。タイムラインにはMovieCliipインスタンス(my_mc)が予めおかれているものとする。ドラッグは3つのマウスイベントを組合わせて扱う[1]⁠。

  1. InteractiveObject.mouseDownイベント(定数MouseEvent.MOUSE_DOWN)のリスナー
    • InteractiveObject.mouseMoveイベントにドラッグのためのリスナーを加える。
    • InteractiveObject.mouseUpイベントにドラッグ終了のリスナーを加える。
  2. InteractiveObject.mouseMoveイベント(定数MouseEvent.MOUSE_MOVE)のリスナー
    • インスタンスの位置をマウスポインタの座標に追随させる。
  3. InteractiveObject.mouseUpイベント(定数MouseEvent.MOUSE_UP)のリスナー
    • InteractiveObject.mouseMoveイベントのリスナーを除く。
    • InteractiveObject.mouseUpイベントのリスナーを除く。

3つのイベントリスナーで、タイムラインに置かれたインスタンス(my_mc)をドラッグできるように組立てたフレームアクションがつぎのスクリプト2だ。

スクリプト2 タイムラインに置かれたインスタンスをドラッグするフレームアクション
// フレームアクション: メインタイムライン
var nOffsetX:Number;
var nOffsetY:Number;
my_mc.addEventListener(MouseEvent.MOUSE_DOWN, xMouseDown);
function xMouseDown(eventObject:MouseEvent):void {
  nOffsetX = my_mc.mouseX;
  nOffsetY = my_mc.mouseY;
  stage.addEventListener(MouseEvent.MOUSE_MOVE, xDrag);
  stage.addEventListener(MouseEvent.MOUSE_UP, xMouseUp);
}
function xDrag(eventObject:MouseEvent):void {
  my_mc.x += my_mc.mouseX - nOffsetX;
  my_mc.y += my_mc.mouseY - nOffsetY;
}
function xMouseUp(eventObject:MouseEvent):void {
  stage.removeEventListener(MouseEvent.MOUSE_MOVE, xDrag);
  stage.removeEventListener(MouseEvent.MOUSE_UP, xMouseUp);
}

スクリプト2について、ふたつ補っておこう。ひとつ目は、InteractiveObject.mouseMoveInteractiveObject.mouseUpイベントのリスナーをStageオブジェクトに加えたことだ。これは、InteractiveObject.mouseDownイベントと異なり、マウスを素速く動かすと、イベントがドラッグしているインスタンスの外で起こってしまう場合に備えた。

ふたつ目は、InteractiveObject.mouseDownイベントのリスナー関数(xMouseDown)が、インスタンスから見たマウスポインタの座標を変数(nOffsetXとnOffsetY)にとっていることだ。なぜなら、InteractiveObject.mouseMoveイベントのリスナー(xDrag())がただマウス座標にインスタンスの位置を合わせれば、ポインタの先にインスタンスの基準点がきてしまう図3⁠。そうならないためには、はじめにクリックしたインスタンス上の座標を覚えておき、ドラッグするときはその分を補正しなければならないのだ。

図3 補正しないとドラッグしたときインスタンスの基準点がマウスポインタの座標にくる
図3 補正しないとドラッグしたときインスタンスの基準点がマウスポインタの座標にくる 図3 補正しないとドラッグしたときインスタンスの基準点がマウスポインタの座標にくる

StarlingフレームワークのDisplayObject.touchイベントを用いたドラッグの扱い

Starlingフレームワークでは、マウス(タッチスクリーンも含めた)操作はすべてDisplayObject.touchイベント(定数TouchEvent.TOUCHで捉える。それがどんな操作だったかは、リスナーが受取る引数のTouchEventオブジェクトからTouchオブジェクトを取出して、そのTouch.phaseプロパティで調べる。

Touch.phaseプロパティの値は文字列で、TouchPhaseクラスに定数として定められている。TouchPhaseクラスの定数とタッチスクリーンおよびマウスの操作は、つぎouchEventオブジェクト.の表2のとおりだ。

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

マウスをドラッグしているときのTouch.phaseプロパティの値はTouchPhase.MOVEDだ。しかも、ActionScript 3.0定義済みのInteractiveObject.mouseMoveイベントとは違い、インスタンスの上でマウスボタンを押したまま動かしていることを示す。つまり、この値だけでドラッグが捉えられ、別にマウスボタンを押したか放したかは気にしなくてよい。

さて、TouchEventオブジェクトからTouchオブジェクトを取出すのが、TouchEvent.getTouch()メソッドだ。引数はふたつある。

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

第1引数は、DisplayObject.touchイベントが起こったかどうか調べる対象のDisplayObjectインスタンスだ。インスタンスに子があれば(つまりDisplayObjectContainerのサブクラスなら⁠⁠、その表示リストを下ってすべての子インスタンスが対象に含まれる。さらに第2引数のTouchPhaseクラス定数で、DisplayObject.touchイベントの操作を絞り込める。デフォルト値はnullで、すべてのマウスあるいはタッチスクリーン操作を調べる。そして、ふたつの引数で定まるTouchオブジェクトが見つかればそのオブジェクト、なければnullが返される。

すると、ドラッグのマウス操作は、前掲スクリプト1のクラス定義(MySprite)につぎのようなスクリプトを加えれば捉えられる。DisplayObject.touchイベントのリスナー関数(mouseOperated)で、TouchEvent.getTouch()メソッドによりTouch.phaseプロパティが定数TouchPhase.MOVEDのTouchオブジェクトを取出している。Touchオブジェクトが得られればnullでなければ⁠⁠、ドラッグ操作が行われていることになる。後は、インスタンスをマウスポインタの位置に動かせばよい。

private function initialize(eventObject:Event):void {
  // ...[中略]...
  instance.addEventListener(TouchEvent.TOUCH, mouseOperated);
}
private function mouseOperated(eventObject:TouchEvent):void {
  var myTouch:Touch = eventObject.getTouch(instance, TouchPhase.MOVED);
  if (myTouch) {
    // インスタンスをマウスポインタに追随させる
  }
}

マウスポインタの座標はプロパティTouch.globalXTouch.globalYで調べられる。ただし、座標がステージから見た値だということに気をつけなければならない[2]⁠。それではドラッグの動きに使えないと思っただろうか。大丈夫だ、問題ない。Touchオブジェクトには、さらに前回のイベントのときのマウスポインタの座標がプロパティとして備わっている。Touch.previousGlobalXTouch.previousGlobalYがそれだ。

すると、これら2種類のマウス座標プロパティの差を求めれば、イベントの間にマウスポインタがどれだけ動いたかわかる。その差の座標値をインスタンスの位置に加えれば、インスタンスはマウスポインタと同じだけ動く。前掲スクリプト2の定義済みActionScript 3.0の場合と違って、インスタンスが初めにクリックされた座標など覚えておかずに済む。

前掲スクリプト1にドラッグのための手を加たのが、以下のスクリプト3のクラス定義(MySprite)だ。もうとくに解説を補うには及ばないだろう。インスタンスをドラッグすると、マウスポインタと同じように動く図4⁠。

図4 ドラッグするとインスタンスがマウスポインタと同じように動く
図4 ドラッグするとインスタンスがマウスポインタと同じように動く 図4 ドラッグするとインスタンスがマウスポインタと同じように動く
スクリプト3 Starlingフレームワークでインスタンスをドラッグする
// ActionScript 3.0クラス定義ファイル: MySprite.as
package  {
  import flash.display.BitmapData;
  import starling.display.Sprite;
  import starling.display.Image;
  import starling.textures.Texture;
  import starling.events.Event;
  import starling.events.TouchEvent;
  import starling.events.Touch;
  import starling.events.TouchPhase;
  public class MySprite extends Sprite{
    private var instance:Image;
    public function MySprite() {
      addEventListener(Event.ADDED_TO_STAGE, initialize);
    }
    private function initialize(eventObject:Event):void {
      var myBitmapData:BitmapData = new Pen();
      var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
      instance = new Image(myTexture);
      addChild(instance);
      instance.addEventListener(TouchEvent.TOUCH, mouseOperated);
      instance.x = stage.stageWidth / 2;
      instance.y = stage.stageHeight / 2;
      instance.pivotX = instance.width / 2;
      instance.pivotY = instance.height / 2;
    }
    private function mouseOperated(eventObject:TouchEvent):void {
      var myTouch:Touch = eventObject.getTouch(instance, TouchPhase.MOVED);
      if (myTouch) {
        var nMoveX:Number = myTouch.globalX - myTouch.previousGlobalX;
        var nMoveY:Number = myTouch.globalY - myTouch.previousGlobalY;
        instance.x += nMoveX;
        instance.y += nMoveY;
      }
    }
  }
}

Starlingフレームワークのドラッグの扱いは、定義済みActionScript 3.0より簡単だった。実は、単純なクリックがむしろやっかいだったりする。さて、いよいよ次回が本連載の最終回だ。Starlingフレームワークのクリックと当たり判定について解説する。最後までよろしくおつき合い願いたい。

今回解説した次のサンプルファイルがダウンロードできます。

おすすめ記事

記事・ニュース一覧