2017-05-26

[Flash] Starling TouchEvent

[Flash] Starlingの学習 TouchEvent

環境:

  • Starling 2.1
  • Adobe AIR SDK 22
  • FlashDevelop 5.2
  • Windows 7 64bit

[話者] StarlingのTouchイベントを学ぶぞ。これはPCでのMouseのClick/Drag、SmartPhoneでのTap,PinchOutなどの操作を検知する。

書籍「Introducing Starling」のサンプルに少し手を加えた。ソースファイルは4つあるよ。

Main.as

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import starling.core.Starling;
    
    /**
     * ...
     * @author foo
     */
    [SWF(width="480",height="480",frameRate="60",backgroundColor="#002143")]
    public class Main extends Sprite 
    {
        private var stln:Starling;
        public static var frameRate:uint = 0;
        
        public function Main() 
        {
            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
            stage.scaleMode = StageScaleMode.SHOW_ALL;
            stage.align = StageAlign.TOP_LEFT;
            stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3dCreated);
            stln = new Starling(Game, stage);
            stln.showStats = true;
            stln.antiAliasing = 0;
            stln.start();
        }
        
        private function onContext3dCreated(e:Event):void 
        {
            stage.stage3Ds[0].removeEventListener(Event.CONTEXT3D_CREATE, onContext3dCreated);

            if (Starling.context.driverInfo.toLowerCase().indexOf("software") != -1) {
                // GPU disable
                Starling.current.nativeStage.frameRate = 30;
            } else {
                // GPU enable
            }
            frameRate = Starling.current.nativeStage.frameRate;
        }
    }
}

Logger.as

package 
{
    import starling.display.Sprite;
    import starling.text.TextField;
    import starling.text.TextFormat;
    
    /**
     * ...
     * @author foo
     */
    public class Logger extends Sprite
    {
        private var tf:TextField;
        private var tx:Vector.<String> = new Vector.<String>();
        private const fontsize:int = 12;
        
        public function Logger(w:int = 480, h:int = 480) 
        {
            tf = new TextField(
                w, h, '',
                new TextFormat('Tahoma', fontsize, 0xf0f0f0, 'left', 'top')
            );
            addChild(tf);
        }
        
        public function add(newtext:String = ''):void 
        {
            var dt:Date = new Date();
            var now:String =
                ('0' + dt.getHours()).substr( -2) + ':' +
                ('0' + dt.getMinutes()).substr( -2) + '.' +
                ('00' + dt.getMilliseconds()).substr( -3);
            tx.push(now + ' ' + newtext);
            var lines:int = tf.height / (fontsize * 1.2);
            while (tx.length >= lines) {
                tx.shift();
            }
            tf.text = tx.join("\n");
        }
    }
}

MyRectangle.as

package 
{
    import starling.display.Quad;
    import starling.display.Sprite;
    
    /**
     * ...
     * @author foo
     */
    public class MyRectangle extends Sprite 
    {
        private var qd:Quad;
        private var rotate_radian:Number;
        
        public function MyRectangle(minLength:int = 240) 
        {
            qd = new Quad(minLength/3, minLength/3);
            qd.setVertexColor(0, 0x000000);
            qd.setVertexColor(1, 0xAA0000);
            qd.setVertexColor(2, 0x00FF00);
            qd.setVertexColor(3, 0x0000FF);
            qd.pivotX = qd.width / 2;
            qd.pivotY = qd.height / 2;
            qd.alpha = 0.5;
            addChild(qd);
            
            rotate_radian = Main.frameRate <= 30 ? 0.02 : 0.01;
        }
        
        public function update():void 
        {
            qd.rotation += rotate_radian;
        }
        
    }

}

Game.as

package 
{
    import flash.geom.Point;
    import starling.display.DisplayObject;
    import starling.display.Quad;
    import starling.display.Sprite;
    import starling.events.EnterFrameEvent;
    import starling.events.Event;
    import starling.events.Touch;
    import starling.events.TouchEvent;
    import starling.events.TouchPhase;
    
    /**
     * ...
     * @author foo
     */
    public class Game extends Sprite 
    {
        private var qd:MyRectangle;
        private var taploc:Point; // tap(click) location
        private var log:Logger;
        
        public function Game() 
        {
            if (stage) init()
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            addEventListener(Event.ENTER_FRAME, onGetGpuStyle);
        }
        
        private function onGetGpuStyle(e:Event):void 
        {
            if (Main.frameRate <= 0) return;
            removeEventListener(Event.ENTER_FRAME, onGetGpuStyle);
            addItem();
        }
        
        private function addItem():void 
        {
            var showStatWidth:int = 100;
            log = new Logger(stage.stageWidth - showStatWidth, stage.stageHeight);
            log.x = showStatWidth; //avoid showStat region
            log.y = 0;
            log.add("framerate per second = " + Main.frameRate);
            if (Main.frameRate <= 30) {
                log.add("GPU disabled.");
            }else{
                log.add("GPU enabled.");
            }
            addChild(log);
            
            addQuad(stage.stageWidth / 2, stage.stageHeight / 2);
            
            addEventListener(EnterFrameEvent.ENTER_FRAME, onFrame);
            taploc = new Point(qd.x, qd.y);
            stage.addEventListener(TouchEvent.TOUCH, onTouchStage);
        }
        
        private function onTouchObj(e:TouchEvent):void 
        {
            var tch:Touch = e.getTouch(stage);
            if ( ! tch) return;
            var ph:String = getTouchPhase(tch);
            log.add("Obj Touch.phase:"+ph);
            if (tch.phase == TouchPhase.ENDED) {
                var tapobj:DisplayObject = e.currentTarget as DisplayObject;
                tapobj.removeEventListeners();
                removeChild(tapobj, true);
                qd = null;
                log.add('removed Quad');
            }
            
        }
        
        private function onTouchStage(e:TouchEvent):void 
        {
            var tch:Touch = e.getTouch(stage);
            if ( ! tch) return;
            var ph:String = getTouchPhase(tch);
            log.add("Stage Touch.phase:"+ph);
            taploc = tch.getLocation(stage);
            if ( ! qd && tch.phase == TouchPhase.BEGAN) {
                addQuad(taploc.x, taploc.y);
            }
        }
        
        private function getTouchPhase(tch:Touch):String
        {
            var ph:String = '';
            if (tch.phase == TouchPhase.BEGAN) {
                ph = 'BEGAN';
            } else if (tch.phase == TouchPhase.ENDED) {
                ph = 'ENDED';
            } else if (tch.phase == TouchPhase.HOVER) {
                ph = 'HOVER';
            } else if (tch.phase == TouchPhase.MOVED) {
                ph = 'MOVED';
            } else if (tch.phase == TouchPhase.STATIONARY) {
                ph = 'STATIONARY';
            }
            return ph;
        }
        
        private function addQuad(ax:Number = 0, ay:Number = 0):void 
        {
            var minLength:int = stage.stageWidth > stage.stageHeight ? stage.stageWidth : stage.stageHeight;
            qd = new MyRectangle(minLength);
            qd.x = ax;
            qd.y = ay;
            addChild(qd);
            qd.addEventListener(TouchEvent.TOUCH, onTouchObj);
            log.add('added Quad');
        }
        
        private function onFrame(e:EnterFrameEvent):void 
        {
            if ( ! qd) return;
            qd.x -= (qd.x - taploc.x) * 0.1;
            qd.y -= (qd.y - taploc.y) * 0.1;
            qd.update();
        }
        
    }

}

TouchEventのログを画面に表示するぞ。四角形はClick(Tap)すると消滅する。消滅後にステージをClick(Tap)すると、四角形が出現する。

実Flash。操作可。

[合いの手] パソコンだと、

  • ステージ上でマウスを動かすだけで、クリックしてないのに TouchPhase.HOVER イベントを送出してるね。
  • クリック開始で TouchPhase.BEGAN イベント、
  • クリック終了の、指を離したとき TouchPhase.ENDED イベント、
  • クリックしたあと指を押しっぱなしでマウスを動かす(マウスドラッグ)すると TouchPhase.MOVED イベント。

スマートフォンだとどうなの?

[話者] 同じswfをAndroid向けにパッケージにする。Androidにapkを転送してインストール。わりと面倒だな。

StarlingTouch1-app.xml

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/22.0">
    <id>samples.android.StarlingTouch1</id>
    <versionNumber>0.0.1</versionNumber>
    <filename>StarlingTouch1</filename>
    <name>
        <text xml:lang="en">Starling Touch 1</text> 
        <text xml:lang="ja">Starlingタッチ1</text> 
    </name>
    <description> 
        <text xml:lang="en">This is an example.</text> 
        <text xml:lang="ja">これはテストです。</text> 
    </description> 
    <copyright>Copyright (c) 2017 Example Co.</copyright> 
    <initialWindow>
        <content>StarlingTouch1.swf</content>
        <renderMode>direct</renderMode>
        <fullScreen>true</fullScreen>
        <autoOrients>true</autoOrients>
    </initialWindow>
    <supportedProfiles>mobileDevice</supportedProfiles>
    <icon> 
        <image16x16>img/icon16x16.png</image16x16> 
        <image32x32>img/icon32x32.png</image32x32> 
        <image48x48>img/icon48x48.png</image48x48> 
        <image128x128>img/icon128x128.png</image128x128>  
        <image512x512>img/icon512x512.png</image512x512>  
    </icon>
    <customUpdateUI>false</customUpdateUI>
    <allowBrowserInvocation>false</allowBrowserInvocation>
    <android>
        <manifestAdditions>
        <![CDATA[
            <manifest>
                <uses-permission android:name="android.permission.INTERNET"/>
            </manifest>
        ]]>
        </manifestAdditions>
    </android>    
</application>
star@b10 MINGW32 /r/201705touch2android$ adt.bat -package -target apk-captive-runtime -storetype pkcs12 -keystore sampleCert.pfx StarlingTouch1.apk StarlingTouch1-app.xml StarlingTouch1.swf img
password: passwd
NOTE: The application has been packaged with a captive runtime.

試したところ、

  • 当然だが、指をスマホに触れずにかざしてもHOVERイベントは送出されない。
  • 四角形をドラッグ(スクラブ)するとMOVEDイベントが発生して四角形を動かせる。
  • 四角形が指についてこないので、四角形と離れた地点のステージをタップ(クリック)できるのだが、そうすると四角形がそこに移動する。
  • ドラッグして指を離したとき、指が四角形の範囲内なら四角形が消滅。ただし四角形を出現させたタップから続けてドラッグして指を離しても消滅しない。これはタップで出現させた一瞬後に(指離したとき)消滅するのを防ぐため。
  • 消滅後にステージをタップででまた四角形が出現。

という感じ。

[合いの手] ようするにHOVERイベントを送出しないだけで、あとはPC版と同じだね。ふーん。

[話者] ログをよく見ると、TouchPhase.BEGANイベントの直前に少しだけTouchPhase.HOVERイベントを送出するようだ。

0 件のコメント:

コメントを投稿

人気記事