2012-11-04

ActionScript3.0でマウス操作を検知する(初歩)

[話者] WindowsとAndroidでゲーム作りたいぞ! ついでにWebブラウザでも体験できるようにしたい。

[合いの手] プログラミング言語は何を選ぶ?

[話者] 以前、少し使ったことのあるActionScript3.0、つまりFlashにするぜ。

AdobeAIRというのを使えば、Windows向けのexe実行ファイルと、Android向けのインストールapkファイルを作れるらしい。もちろんFlashだからWebブラウザでもswfファイルを実行できる。

[合いの手] Flash作るソフトは持っているのか?

[話者] FlashDevelop 4.0.4 と Flex SDK 4.6の無料コンビを使うぜ。Flash Professional CS6も購入したが使い方は後で調べよう。

Flashで私が作りたいのはインタラクション interaction のある、つまり操作できるアプリだ。操作するには、ブラウザとWindowsアプリなら、マウスとキーボードを感知しなければ。というわけで、マウス操作を認識するアプリを作ってみる。

アドビのWebサイト http://livedocs.adobe.com/flash/9.0_jp/ActionScriptLangRefV3/flash/events/MouseEvent.html の最下部にあるサンプルを少し変えて、以下のようにしたぞ。

実際に動作するFlashはコレだ。

https://sites.google.com/site/itouhiro/2012/20121104mouseEvent.swf

動作は‥‥以下がある。

  • マウスカーソルを動かして、オブジェクト上に乗せる MOUSE_OVER, ROLL_OVER
  • マウスカーソルをオブジェクト上に乗せた後で、ホイールを動かす MOUSE_WHEEL
  • マウスカーソルを動かして、オブジェクトの外に出す MOUSE_OUT, ROLL_OUT
  • マウスカーソルをオブジェクト上に乗せた後で、左クリック(押したままに)する MOUSE_DOWN
  • マウスカーソルをオブジェクト上で押したまま、ドラッグする MOUSE_MOVE
  • マウスカーソルをオブジェクト上で押した状態のとき、離す MOUSE_UP, MOUSE_CLICK

アドビのWebサイトではtrace()でログ文を出力していたけど、こちらでは画面上に表示している。

[合いの手] ソースが3分割されているのか。こういうのって、どこから読めばいいかわからないなー。

[話者] 実行される順に読むのが素直じゃないか? まずMainクラスの、Mainコンストラクタが実行されてその中でinit()メソッドが呼ばれる。まず、Mainクラス。単にLoggerクラスとChildSpriteクラスのインスタンスを生成して、それをaddChild()してるだけ。

Main.as

package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    
    /**
     * ...
     * @author itouhiro
     */
    [SWF(backgroundColor="0xf8f8f8", width="320",height="240", frameRate="15")]
    public class Main extends Sprite 
    {
        
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            var logger:Logger = new Logger('START!!');
            addChild(logger);
            
            var myObj:ChildSprite = new ChildSprite(logger);
            myObj.x = stage.stageWidth / 2;
            myObj.y = stage.stageHeight / 2;
            addChild(myObj);
        }
        
    }
    
}

addChild()するとDisplayListに追加されて、すなわち画面に表示されるようになる。loggerというのはSTART!!と表示されてる文字の部分で、myObjというのが四角形。

[合いの手] 冒頭のpackageって何だっけ?

[話者] 複数のクラスをまとめる。たとえばオープンソースで他人に使ってほしいライブラリを公開する場合、package com.YourDomain/yourLibrary と書いておけばいい、らしい。

[合いの手] [SWF(backgroundColor="0xf8f8f8", width="320",height="240", frameRate="15")] って何だっけ?

[話者] メタデータ。Flash Proffessional使わなくても、mxmlcにコマンドライン渡さなくても、swfのサイズとか秒あたりのフレーム数を指定できる。ドキュメントクラスの直前に書く。

[合いの手] ドキュメントクラス?

[話者] 最初に実行されるクラス。(swfを実行するとき、ドキュメントクラスのコンストラクタがまず呼び出される)

上のFlashDevelopの画像右側を見てほしい。Main.asと書かれた左下に小さなアイコンで↓とあるだろう。これがドキュメントクラスの印。右クリックして、別のファイルをドキュメントクラスに指定することもできるぞ。

[合いの手] LoggerクラスとかChildSpriteクラスは、明示的に読み込んでないんだけど、自動的に読み込まれる?

[話者] ドキュメントクラスと同じ階層のフォルダにあるasファイルは自動的に読み込まれる。ドキュメントクラスと違う階層にある場合、たとえば Logger.asファイルが com/YourDomain/yourLibrary/Logger.as という階層にあるなら

import com.YourDomain.yourLibrary.Logger と書けば、その階層の Logger.asファイルを読み込む。

次にLoggerクラス。log()メソッドでログ文字列を追加されたら、画面に表示されている内容を書き換える。

Logger.as

package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    
    /**
     * ...
     * @author itouhiro
     */
    public class Logger extends Sprite
    {
        private var tf:TextField;
        private var lines:Array;
        
        public function Logger(str:String = ''):void
        {
            var format:TextFormat = new TextFormat();
            format.font = "Courier New";
            format.size = 12;
            format.color = 0x999999;
            
            tf = new TextField();
            tf.x = 10;
            tf.y = 0;
            tf.defaultTextFormat = format;
            tf.text = str;
            tf.autoSize = TextFieldAutoSize.LEFT;
            addChild(tf);
            
            lines = new Array();
        }
        
        public function log(str:String):void
        {
            lines.push(new Date().time.toString().substr(-5) + ' ' + str);
            if (lines.length >= 15)
            {
                lines.shift();
            }
            tf.text = lines.join('\n');
        }
    
    }

}

[合いの手]import flash.text.TextFieldAutoSize;とかは全部 手で書いてるわけ?

[話者] いや、FlashDevelop使っていれば、import文は手で書く必要が無い。

テキストカーソルを適切な位置(このソースだとTextFieldAutoSize.LEFTのどこか)に置いて、[Refactor - Code Generator] (Ctrl+Shift+1) 押せば、勝手にimport文を追加してくれる。Ctrl+Shift+1 は押しにくいので Ctrl+Jにしているがな。

[合いの手] private var tf:TextField; は手で入力した?

[話者] いや、tf = new TextField()のtfにカーソルを置いて[Refactor - Code Generator]。

[合いの手] そもそも、このファイル冒頭のpackage {は手で入力した?

[話者] いや、Main.asの var logger:Logger = new Logger('START!!')のLoggerにカーソルを置いて[Refactor - Code Generator]。そのとき extends (Base Class)をマウスで指定する必要はあったけどな。

[合いの手] substr(-5)ってなんだ?

[話者] 「as3 substr」でGoogle検索すると http://livedocs.adobe.com/flash/9.0_jp/ActionScriptLangRefV3/String.html#substr() が見つかるぞ。文字列の最後5文字だけ取り出すという意味だ。 Date().time.toString()で現在時刻を1970/1/1からのミリ秒の文字列にする。最後の3ケタがミリ秒で、上位2ケタは秒ということになる。

[合いの手] var format:TextFormat = new TextFormat()のTextFormatは2回書く必要があるのか? 一度で済ましたいのだが

[話者] var format = new TextFormat() と書くこともできるようだ。しかしその場合 17 Warning: variable 'format' has no type declaration. という警告がでるのだ。

var sizeS:uint = 50;

var sizeS:uint; sizeS = 50; の2行をひとつにまとめたものであることからも、2回書いたほうがよい。

最後にChildSpriteクラス。マウス操作を検知する、四角形なオブジェクトだ。

ChildSprite.as

package  
{
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    
    /**
     * ...
     * @author itouhiro
     */
    public class ChildSprite extends Sprite 
    {
        private var logger:Logger;
        private var sizeS:uint = 50; //stop
        private var colorS:uint = 0xffcc00;
        private var sizeO:uint = 60; //over
        private var colorO:uint = 0xccff00;
        private var colorD:uint = 0x00ccff; //down
        
        public function ChildSprite(arg_logger:Logger) 
        {
            draw(sizeS, sizeS, colorS);
            addEventListener(MouseEvent.CLICK, clickHandler);
            addEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler);
            addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            //addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
            addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
            addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
            addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
            addEventListener(MouseEvent.ROLL_OUT, rollOutHandler);
            addEventListener(MouseEvent.ROLL_OVER, rollOverHandler);
            //addEventListener(MouseEvent.CONTEXT_MENU, contextMenuHandler);
            //addEventListener(MouseEvent.MIDDLE_CLICK, middleClickHandler);
            //addEventListener(MouseEvent.MIDDLE_MOUSE_DOWN, middleMouseDownHandler);
            //addEventListener(MouseEvent.MIDDLE_MOUSE_Up, middleMouseUpHandler);
            //addEventListener(MouseEvent.RIGHT_CLICK, rightClickHandler);
            //addEventListener(MouseEvent.RIGHT_MOUSE_DOWN, rightMouseDownHandler);
            //addEventListener(MouseEvent.RIGHT_MOUSE_UP, rightMouseUpHandler);
            
            logger = arg_logger;
        }
        
        private function draw(w:uint, h:uint, bgColor:uint):void 
        {
            graphics.clear();
            graphics.beginFill(bgColor);
            graphics.drawRect(0, 0, w, h);
            graphics.endFill();
        }
        
        private function rightMouseUpHandler(e:MouseEvent):void 
        {
            logger.log('RIGHT_MOUSE_UP');
        }
        
        private function rightMouseDownHandler(e:MouseEvent):void 
        {
            logger.log('RIGHT_MOUSE_DOWN');
        }
        
        private function rightClickHandler(e:MouseEvent):void 
        {
            logger.log('RIGHT_CLICK');
        }
        
        private function middleMouseUpHandler(e:MouseEvent):void 
        {
            logger.log('MIDDLE_MOUSE_UP');
        }
        
        private function middleMouseDownHandler(e:MouseEvent):void 
        {
            logger.log('MIDDLE_MOUSE_DOWN');
        }
        
        private function middleClickHandler(e:MouseEvent):void 
        {
            logger.log('MIDDLE_CLICK');
        }
        
        private function contextMenuHandler(e:MouseEvent):void 
        {
            logger.log('CONTEXT_MENU');
        }
        
        private function rollOverHandler(e:MouseEvent):void 
        {
            logger.log('ROLL_OVER');
        }
        
        private function rollOutHandler(e:MouseEvent):void 
        {
            logger.log('ROLL_OUT');
        }
        
        private function mouseWheelHandler(e:MouseEvent):void 
        {
            logger.log('MOUSE_WHEEL  delta:' + e.delta);
        }
        
        private function mouseUpHandler(e:MouseEvent):void 
        {
            logger.log('MOUSE_UP');
            var spr:Sprite = Sprite(e.target);
            spr.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            spr.stopDrag();
            draw(sizeO, sizeO, colorO);
        }
        
        private function mouseOverHandler(e:MouseEvent):void 
        {
            logger.log('MOUSE_OVER');
            draw(sizeO, sizeO, colorO);
        }
        
        private function mouseOutHandler(e:MouseEvent):void 
        {
            logger.log('MOUSE_OUT');
            draw(sizeS, sizeS, colorS);
        }
        
        private function mouseMoveHandler(e:MouseEvent):void 
        {
            logger.log('MOUSE_MOVE');
            e.updateAfterEvent();
        }
        
        private function mouseDownHandler(e:MouseEvent):void 
        {
            logger.log('MOUSE_DOWN');
            draw(sizeO, sizeO, colorD);
            var spr:Sprite = Sprite(e.target);
            spr.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
            spr.startDrag();
        }
        
        private function doubleClickHandler(e:MouseEvent):void 
        {
            logger.log('DOUBLE_CLICK');
        }
        
        private function clickHandler(e:MouseEvent):void 
        {
            logger.log('CLICK');
        }
        
    }

}

[合いの手] private function clickHandler(e:MouseEvent):void とかを手で入力した?

[話者] いや、addEventListener(MouseEvent.CLICK, clickHandler)のclickHandlerにカーソルを置いて[Refactor - Code Generator]。

[合いの手] MouseEvent.RIGHT_MOUSE_DOWN とか書かれたaddEventListenerをコメント化してあるけど、これはなぜ?

[話者] Flex SDK 4.6 (FlashPlayer 11.3)では非対応で、コンパイルエラー(Error: Access of possibly undefined property RIGHT_MOUSE_DOWN )に なるからコメント化してある。 http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/MouseEvent.html を読む限り、FlashPlayer 11.2 以降は RIGHT_MOUSE_DOWN に対応してるはずなんだけどね‥‥なぜだろう

[合いの手] メソッド mouseDownHandler() の中で var spr:Sprite = Sprite(e.target) とあるけど new がないね。

[話者] これはSpriteオブジェクトを新規生成してるわけじゃなくて、すでに存在する e.targetのオブジェクトを再選択してるようだな。そして addEventListener() と startDrag() のメソッドを実行してる。

[合いの手] ドラッグしたままマウスをFlashステージ外に出した場合、オブジェクトが消えて、マウスをFlashステージ内に戻すとオブジェクトが復活するね。でマウスをドラッグしてないのに、ドラッグになってる。これって、どう実現してるのかな?

[話者] ‥‥このソースコードのようにすれば動作するんだろうな。

別のソース書いてstartDrag()試してみたぞ。

実際のFlashはコレだ。

https://sites.google.com/site/itouhiro/2012/20121104mouseEvent2.swf

http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Sprite.html#startDrag() のサンプルを使った。

Main.as

package 
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    
    /**
     * ...
     * @author itouhiro
     */
    [SWF(backgroundColor="0xf8f8f8", width="320",height="240", frameRate="15")]
    public class Main extends Sprite 
    {
        private var circle:Sprite;
        
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            circle = new Sprite();
            circle.graphics.beginFill(0xFFCC00);
            circle.graphics.drawCircle(0, 0, 40);
            addChild(circle);
            
            circle.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            circle.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
            
        }
        
        private function mouseUpHandler(e:MouseEvent):void 
        {
            circle.stopDrag();
        }
        
        private function mouseDownHandler(e:MouseEvent):void 
        {
            circle.startDrag();
        }
        
    }
    
}

[合いの手] これも、ドラッグしたままマウスをFlashステージ外に出してもオブジェクトが正しくついてくるね。ふーん。

0 件のコメント:

コメントを投稿

人気記事