2017-05-27

[Flash] Starling Texture

[Flash] Starlingの学習 Texture

EventのBubblingについても学ぶ。

環境:

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

[話者] StarlingのTextureについて、書籍『Introducing Starling』で学ぶぞ。

まずはテクスチャー画像を画像編集ソフトで作成する。 32x64 pixelの8bit PNGファイル。

ソースコードは書籍とほぼ同じ。

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.NO_SCALE;
            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;
        }
    }
}

Game.as

package 
{
    import flash.display.Bitmap;
    import starling.display.Image;
    import starling.display.Sprite;
    import starling.events.Event;
    import starling.textures.Texture;
    import starling.utils.deg2rad;
    
    /**
     * ...
     * @author foo
     */
    public class Game extends Sprite 
    {
        private const NUM_BRICK:uint = 80;
        private var bricks:Vector.<Image> = new Vector.<Image>(NUM_BRICK, true);
        // current dir 'Proj/src/'
        [Embed(source = "../bin/201705brick1.png")]
        private static const Brick:Class;
        
        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 brickBitmap:Bitmap = new Brick();
            var texture:Texture = Texture.fromBitmap(brickBitmap);
            for (var i:int = 0; i < NUM_BRICK; i++) {
                var img:Image = new Image(texture);
                img.alpha = Math.random();
                img.x = Math.random() * stage.stageWidth;
                img.y = Math.random() * stage.stageHeight;
                img.rotation = deg2rad(Math.random() * 360);
                img.color = (Math.random() * 0xEE) << 16 | (Math.random() * 0xEE) << 8 | (Math.random() * 0xEE);
                
                addChild(img);
                bricks[i] = img;
            }
        }
    }
}

こんな表示になる。

ポイントは Texture.fromBitmap(brickBitmap);の行をloopの外で実行したこと。もしloopの中で実行すると、loop毎に画像をGPUに転送するのでメモリを無駄に使うし、毎回mipmap生成で遅くもなる。

画像ファイルをswfに埋め込んだのが先ほどだ。 次はPNG画像ファイルを外部から読み込んで表示することにする。

Main.asは同じ。

Game.asの変更点は

--- /r/201705texture1/src/Game.as   2017-05-30 10:06:39.781823200 +0900
+++ /r/201705texture2/src/Game.as   2017-05-30 11:34:10.479145900 +0900
@@ -3,2 +3,4 @@
    import flash.display.Bitmap;
+   import flash.display.Loader;
+   import flash.net.URLRequest;
    import starling.display.Image;
@@ -17,5 +19,3 @@
        private var bricks:Vector.<Image> = new Vector.<Image>(NUM_BRICK, true);
-       // current dir 'Proj/src/'
-       [Embed(source = "../bin/201705brick1.png")]
-       private static const Brick:Class;
+       private var loader:Loader;
        
@@ -37,9 +37,13 @@
            removeEventListener(Event.ENTER_FRAME, onGetGpuStyle);
-           addItem();
+           // current dir 'Proj/bin/'
+           loader = new Loader();
+           loader.load(new URLRequest("201705brick1.png"));
+           loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
        }
        
-       private function addItem():void 
+       private function onLoadComplete(e:Object):void 
        {
-           var brickBitmap:Bitmap = new Brick();
-           var texture:Texture = Texture.fromBitmap(brickBitmap);
+           loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoadComplete);
+           var loadedBitmap:Bitmap = loader.content as Bitmap;
+           var texture:Texture = Texture.fromBitmap(loadedBitmap);
            for (var i:int = 0; i < NUM_BRICK; i++) {

ポイントは private function onLoadComplete(e:Object):void の行。

本に書かれてるとおり (e:Event) にすると

Error #1034: Type Coercion failed: cannot convert flash.events::Event to starling.events.Event (強制型変換に失敗しました)

というエラーになる。これはflash.display.Loaderクラスが、starling.events.Eventじゃなくてflash.events.Eventを送出するから。

これを使い分けるには、ソースコードでimport flash.events.Eventを追加して、ソースコードのEventの文字列すべてをflash.events.Eventまたはstarling.events.Eventと書き分ければいいはず。 でも面倒なので、flash.events.Eventはe:Objectで受けることにする。

参考: https://forum.starling-framework.org/topic/error-cant-convert-starlingeventsevent-to-flasheventsevent

実行結果としては先ほどと同じ。

[合いの手] これは表示するだけ?

[話者] そうだよ。 次に、クリック(タップ)の検知方法はEventのBubblingを使うとラク、と書籍にある。確認してみる。

変更点は以下。

--- /r/201705texture2/src/Game.as   2017-05-30 11:34:10.479145900 +0900
+++ /r/201705texture3/src/Game.as   2017-06-01 20:54:38.918072900 +0900
@@ -5,2 +5,3 @@
    import flash.net.URLRequest;
+   import starling.display.DisplayObject;
    import starling.display.Image;
@@ -8,3 +9,7 @@
    import starling.events.Event;
+   import starling.events.Touch;
+   import starling.events.TouchEvent;
+   import starling.events.TouchPhase;
    import starling.textures.Texture;
+   import starling.textures.TextureSmoothing;
    import starling.utils.deg2rad;
@@ -55,2 +60,3 @@
                img.color = (Math.random() * 0xEE) << 16 | (Math.random() * 0xEE) << 8 | (Math.random() * 0xEE);
+               img.textureSmoothing = TextureSmoothing.NONE;
                
@@ -59,2 +65,16 @@
            }
+           stage.addEventListener(TouchEvent.TOUCH, onTouch);
+       }
+       
+       private function onTouch(e:TouchEvent):void 
+       {
+           var touchPoints:Vector.<Touch> = e.getTouches(this);
+           var touchedItem:DisplayObject = e.currentTarget as DisplayObject;
+           
+           if (touchPoints.length != 1) return;
+           
+           var theTouchPoint:Touch = touchPoints[0];
+           if (theTouchPoint.phase == TouchPhase.ENDED) {
+               trace(e.currentTarget, e.target, e.bubbles);
+           }
        }

[合いの手] テトリスのブロックみたいのをクリックすると、traceでなにやら表示するね。

[話者] EventのBubblingについては

http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.html の以下の箇所がわかりやすいな。

StarlingのEventはBubblingはあるが、Captureはない。 で、今回の場合、テトリスのようなブロックをクリックすると

  • e.currentTarget はstage。これはaddEventListenerしていてEventを受け取ったやつだ。
  • e.target はテトリスのようなブロック。 これはクリックされたやつだ。
  • e.bubbles は後で説明する。

最初はe.targetがクリックEventを受け取り、それがBubblingで浮き上がっていって、stageもクリックEventを受け取ったんだ。

[合いの手] なるほど。クリックを監視したのはstageだけなのに、どのオブジェクトをクリックしたのか判明するのか。

個々のオブジェクトひとつひとつを監視(個々のObjectにaddEventListenerを付ける)しなくてもいい、ということだね。

[話者] そう。

ちなみにActionScript3.0のflash.events.Eventは、capture段階では受け取らないのがデフォルト。受け取るには addEventListenerの第3引数を指定する必要がある。

また、先ほどのtraceにあった e.bubbles なんだけど、 「e.currentTargetが受け取ったEventはBubblingで受け取ったものですか? Yes」という意味‥‥と書籍には書いてあったが http://www.adobe.com/devnet/actionscript/articles/event_handling_as3.htmlhttp://doc.starling-framework.org/current/starling/events/Event.html を見ると違って、BubblingするタイプのEventなら実際にBubblingしようがしまいがtrueになるらしい。TouchEvent.TOUCH はBubblingするタイプらしい。

0 件のコメント:

コメントを投稿

人気記事