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

第63回 Starlingフレームワークでインスタンスのクリックをどう扱うか

この記事を読むのに必要な時間:およそ 7 分

矩形でないビットマップのクリックを捉える

Starlingフレームワークでは,インスタンスと座標の重なりは,矩形領域で評価される。定義済みActionScript 3.0と異なり,矩形でないかたちについてピクセル単位で調べることはできない。だが,望みはある。定義済みActionScript 3.0が使えればよいのだ。ビットマップなら,まさにその手が使える。そこで,[ライブラリ]にアルファで抜いたビットマップを用意して,クラスを定めよう図3⁠。

図3 [ライブラリ]の矩形でないビットマップにクラスを定める

図3 [ライブラリ]の矩形でないビットマップにクラスを定める

もちろん,前掲スクリプト1をそのまま使うと,矩形領域内であればアルファで抜かれたピクセル上もクリックを捉えてしまう図4⁠。

図4 矩形領域内はアルファで抜かれていてもクリックを捉える

図4 矩形領域内はアルファで抜かれていてもクリックを捉える

しかし,BitmapDataクラスにはBitmapData.hitTest()メソッドがあり,アルファを識別した座標の重なりが確かめられる。このメソッドにはいくつかの使い方がある※1⁠。今回は,つぎの構文で用いる。

BitmapDataオブジェクト.hitTest(左上角Pointオブジェクト, アルファしきい値, 対象Pointオブジェクト)

第1引数はインスタンスの左上角座標を,Pointオブジェクトで渡す。BitmapDataインスタンスはタイムラインに直には置けないため,xy座標をプロパティとしてもたないからだ。第2引数は,インスタンスのピクセルの有無を切り分けるアルファの値(しきい値)で,256階調(0~255)の数値によって定める。第3引数が,重なりを調べる座標のPointオブジェクトだ。

前掲スクリプト1に手を加えて,つぎのようにBitmapData.hitTest()メソッドを呼出してみよう。メソッドの第3引数には,インスタンスから見たマウスポインタの座標をPointオブジェクト(mousePoint)で渡した。すると,第1引数の左上角は原点(0, 0)(zeroPoint)でよい。第2引数のアルファは,完全に抜けたピクセルの値として0x0(0)を渡している。なお,BitmapDataインスタンスはリスナーメソッドからも参照するため,ローカル変数でなく,プロパティ(myBitmapData)としてvar宣言した。

private var myBitmapData:BitmapData;
private var zeroPoint:Point = new Point(0, 0);
private function initialize(eventObject:Event):void {
  // var myBitmapData:BitmapData = new Pen();
  myBitmapData = new Pen();
  // ...[中略]...
}
private function mouseOperated(eventObject:TouchEvent):void {
  var myTouch:Touch = eventObject.getTouch(instance, TouchPhase.ENDED);
  if (myTouch) {
    var mousePoint:Point = myTouch.getLocation(instance);
    // if (instance.hitTest(mousePoint, true)) {
    if (myBitmapData.hitTest(zeroPoint, 0x0, mousePoint)) {
      instance.rotation += angle;
    }
  }
}

これで,アルファの抜けた矩形領域内でマウスボタンを放しても,クリックとは捉えられないようになる。ただし,アルファの抜けた矩形領域でマウスボタンを押して,ビットマップイメージ上で放すとクリックになってしまう図5⁠。これは矩形領域内でマウスボタンを押す操作が,DisplayObject.touchイベントTouch.phaseプロパティはTouchPhase.BEGANを起こすからだ。

図5 矩形領域でマウスボタンを押してビットマップイメージ上で放すとクリックになる

図5 矩形領域でマウスボタンを押してビットマップイメージ上で放すとクリックになる

つまり,マウスボタンを押したときTouch.phaseプロパティがTouchPhase.BEGANも,アルファを識別してポインタ座標がビットマップ上かどうかを確かめなければならない。つぎのスクリプト2は,TouchEvent.getTouch()メソッドの第2引数なしにTouchオブジェクトを取出し※2⁠,switchステートメントでTouch.phaseプロパティがTouchPhase.BEGANTouchPhase.ENDEDの処理をそれぞれ定めた。

スクリプト2 ビットマップのアルファも識別してクリックを捉える

// 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;
  import flash.geom.Point;
  public class MySprite extends Sprite{
    private var instance:Image;
    private var angle:Number = 15 * Math.PI / 180;
    private var myBitmapData:BitmapData;
    private var zeroPoint:Point = new Point(0, 0);
    private var pressed:Boolean = false;
    public function MySprite() {
      addEventListener(Event.ADDED_TO_STAGE, initialize);
    }
    private function initialize(eventObject:Event):void {
      myBitmapData = 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);
      if (myTouch) {
        switch (myTouch.phase) {
          case TouchPhase.BEGAN:
            if (instanceIsTouched(myTouch)) {
              pressed = true;
            }
            break;
          case TouchPhase.ENDED:
            if (pressed && instanceIsTouched(myTouch)) {
              instance.rotation += angle;
            }
            pressed = false;
            break;
        }
      }
    }
    private function instanceIsTouched(myTouch:Touch):Boolean {
      var mousePoint:Point = myTouch.getLocation(instance);
      var touched:Boolean = myBitmapData.hitTest(zeroPoint, 0x0, mousePoint);
      return touched;
    }
  }
}

座標がアルファを識別したビットマップイメージ上かどうかは,別に定めた関数(instanceIsTouched())で調べることにした。DisplayObject.touchイベントのリスナーメソッド(mouseOperated())は,インスタンスの矩形領域でマウスボタンを押したときTouch.phaseプロパティがTouchPhase.BEGAN⁠,ポインタの座標がビットマップ上ならフラグのプロパティ(pressed)trueにする。そして,マウスボタンを放したときTouch.phaseプロパティはTouchPhase.ENDED⁠,このプロパティ値がtrueかつマウスポインタの座標がビットマップ上であることを確かめたうえで,クリックとして扱っている。

これで,ビットマップイメージのアルファも識別したマウスクリックが捉えられる。さて,少しばかり手間がかかってしまった。Starlingにかぎらず,フレームワークを使うときには,こうした得意・不得意や特徴を理解しておくことが大切だ。

※1
BitmapData.hitTest()メソッドの構文や文法について,詳しくは「Starlingフレームワークでビットマップ上のクリックを検知する」02BitmapData.hitTest()メソッドで座標との重なりを調べるを参照してほしい。
※2
前掲スクリプト2のリスナーメソッド(mouseOperated)は,TouchEvent.getTouch()メソッドで取出したTouchオブジェクトを一旦ifステートメントの条件で調べている。これは,インスタンスに重ねたマウスポインタTouch.phaseプロパティはTouchPhase.HOVERをインスタンスの外に出したとき,メソッドの戻り値がnullになるためだ。

最後に

Flash Player 9から始まった5年を超える本連載は今回で閉じことになる。5年間でFlashの技術も取り巻く状況も,大きく変わった。また,今なお変わり続けている。Flashを始めとするWebの技術については,筆者のサイトFumioNonaka.comで引続き発信していく。折に触れて,ご覧いただければ幸いだ。最後に改めて,長い間のご愛読に感謝したい。

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書