あたごんとFlash-as3isolib


さて、ActionScript3.0(as3)を使ってアイソメトリックな世界を簡単に作れるライブラリー「as3isolib」の解説7回目。
概要やインストール方法については解説第一回目 を見てみてください。


今回は、プログラミングから少し離れて、仕事でas3isolibを使ったときに一度は調査したりいじったりするであろう、前後判定&表示ロジック部分の解説をしたいと思います。
これを理解しておけば、「すいません。なぜか○○の時、後ろに表示されちゃう△△を手前に表示してくれませんか?」なんて言われたときも慌てずに済みます。


その肝心のロジックは
  as3isolib.display.renderers
パッケージにある
  DefaultSceneLayoutRenderer
クラスの
  public function renderScene (scene:IIsoScene):void
に記述されています。

IsoSceneのrenderメソッドを呼ぶ度に、このrenderSceneメソッドが呼ばれます。
処理の流れとしては、

1. 全てのオブジェクト同士を比較して、前後関係を判定
2. 判定結果に合わせて、表示順を変更

となっています。


1. 全てのオブジェクト同士を比較して、前後関係を判定 について

renderSceneメソッドのなかではこのように判定しています。

var rightA:Number = objA.x + objA.width;
var frontA:Number = objA.y + objA.length;
var topA:Number = objA.z + objA.height;

if ((objB.x < rightA) &&
    (objB.y < frontA) &&
    (objB.z < topA) &&
    (i !== j))
{
    // AよりBが後ろだと判定したら、配列に入れる
    behind.push(objB);
}


width, length, heightや、オブジェクトAの座標(x, y, z)の場所(Aの箱の真ん中にある青い点)、オブジェクトBの座標(x, y, z)の場所(Bの箱の真ん中にある白い点)は次の図の通りです。


Adobe Flash上級講座

これは、要するに、次の様なエリアより、Bの座標が奥だった場合に、AはBより手前。と判定していると言うことです。



Adobe Flash上級講座
今は、丁度 objB.x == objA.x + objA.width
なのでBはAより手前に表示されています。

試しに、objB.xを < objA.x + objA.width な位置に移動してみると。。。


Adobe Flash上級講座
確かにBがAの奥に行きました!
A( x, width ) = ( 25, 50 ) : objA.x + obj.width = 75
B( x ) = ( 70 )



次は、どう効率よく、前後判定結果を描画に反映するかについて説明したいところですが、今日は細々した話が続いたので、次回に説明します!
お楽しみに~
あたごんとFlash-as3isolib


さて、ActionScript3.0(as3)を使ってアイソメトリックな世界を簡単に作れるライブラリー「as3isolib」の解説6回目。
概要やインストール方法については解説第一回目 を見てみてください。

今回は、本流からちょっぴり横道にそれて、デバッグで活躍するカラーフィルターについて説明します。
これを使うと次の様に、箱に好きな色を付けることが出来ます。

あたごんとFlash-色はこ
ソースはこんな感じ

package
{
    import as3isolib.display.IsoView;
    import as3isolib.display.primitive.IsoBox;
    import as3isolib.display.scene.IsoScene;
    import as3isolib.graphics.SolidColorFill;
    
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    
    public class IsoColorSample extends Sprite
    {
        public function IsoColorSample()
        {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;


            // シーンの作成
            var isoScene:IsoScene = new IsoScene();
            // 真ん中に表示させたいので、IsoViewを使います
            var isoView:IsoView = new IsoView();
            isoView.addScene( isoScene );
            addChild( isoView );
            
            // 箱の作成
            var box:IsoBox = new IsoBox();
            // 箱の色をセット。引数は、色(uint)と、α値(Number)
            box.fill = new SolidColorFill(0xFF0000, 1);
            isoScene.addChild( box );

            // レンダリング
            isoView.render( true );
        }
    }
}


fillパラメータに、IFillインタフェースを継承した、SolidColorFillをセットすることで色がつけられます。
IsoBoxの場合は六面全てに違う色を指定することも出来ます。
その場合は、fillsパラメータに配列をセットします。


// 面はindex=0から、上面、右面、左面、背面右、背面左、底です。
// 6面色を付けてもよく分からないので3面だけ。
box.fills = [ new SolidColorFill(0xFF0000, 1), new SolidColorFill(0x0000FF, 1), new SolidColorFill(0x00FF00, 1) ];

あたごんとFlash-色はこ2

Nintendo64のロゴみたいな箱ができました。

これを組み合わせれば次の様な面白い画像も作成できます。
あたごんとFlash-まりおさん
ソースは次の通りです。


package
{
    import as3isolib.display.IsoView;
    import as3isolib.display.primitive.IsoBox;
    import as3isolib.display.scene.IsoScene;
    import as3isolib.graphics.SolidColorFill;
    
    import flash.display.Sprite;
    
    public class IsoColorSample2 extends Sprite
    {
        public function IsoColorSample2()
        {
            var isoScene:IsoScene = new IsoScene();
            var isoView:IsoView = new IsoView();
            isoView.addScene( isoScene );
            // カメラを動かして、真ん中に表示される様にする
            isoView.pan( 40, -30 );
            addChild( isoView );
            
            // ドットの色。赤、黄土色、肌色
            var colors:Vector.<uint> = new <uint>[ 0xdc2900, 0x8b7300, 0xffa53c ];
            // ドット絵の行数
            var ROW_NUM:uint = 17;
            // ドット絵の列数
            var COL_NUM:uint = 12;
            // ドット絵
            var maps:Vector.<int> = new <int>[
                -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                -1, -1, -1, 0, 0, 0, 0, 0, -1, -1, -1, -1,
                -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1,
                -1, -1, 1, 1, 1, 2, 2, 1, 2, -1, -1, -1,
                -1, 1, 2, 1, 2, 2, 2, 1, 2, 2, 2, -1,
                -1, 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 2,
                -1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, -1,
                -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, -1, -1,
                -1, -1, 1, 1, 0, 1, 1, 1, -1, -1, -1, -1,
                -1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, -1,
                 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
                 2, 2, 1, 0, 2, 0, 0, 2, 0, 1, 2, 2,
                 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2,
                 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2,
                -1, -1, 0, 0, 0, -1, -1, 0, 0, 0, -1, -1,
                -1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, -1,
                 1, 1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1
            ];
            
            
            // ドット絵を描く
            for ( var row:int = ROW_NUM-1; row >= 0; row-- )
            {
                for ( var col:int = 0; col < COL_NUM; col++ )
                {
                    var colorIndex:int = maps[ row*COL_NUM + col ];
                    if ( colorIndex != -1 )
                    {
                        var isoBox:IsoBox = new IsoBox();
                        var color:uint = colors[ colorIndex ];
                        // はみ出るので、箱のサイズを8x8x8にする
                        isoBox.setSize( 8, 8, 8 );
                        // 箱の色を指定
                        isoBox.fill = new SolidColorFill( color, 1 );
                        isoBox.x = isoBox.width * col;
                        isoBox.z = ( ROW_NUM-1 - row ) * isoBox.height;
                        isoScene.addChild( isoBox );
                    }
                }
            }
            
            // 最後に描画
            isoView.render( true );
        }
    }
}


以上で、カラーフィルターについての説明は終了です!
次回は、ちょっとライブラリーの深い部分について説明していきたいと思います。お楽しみに~
あたごんとFlash-as3isolib


さて、ActionScript3.0(as3)を使ってアイソメトリックな世界を簡単に作れるライブラリー「as3isolib」の解説5回目。
概要やインストール方法については解説第一回目 を見てみてください。


今回は、好きな画像をas3isolibで扱う方法について説明します。

好きな画像を表示させるために、as3isolibでは専用のクラス、IsoSpriteクラスが用意されています。
早速、ゲーム界で世界一有名な次の画像を、as3isolib上に表示させてみましょう。

あたごんとFlash-まりお

package
{
    import as3isolib.display.IsoSprite;
    import as3isolib.display.scene.IsoGrid;
    import as3isolib.display.scene.IsoScene;
    
    import flash.display.Bitmap;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.net.URLRequest;
    
    public class IsoSpriteSample extends Sprite
    {
        private var _isoScene:IsoScene;

        public function IsoSpriteSample()
        {
            stage.scaleMode = StageScaleMode.NO_SCALE;

            // シーンを作る
            _isoScene = new IsoScene();
            // displayListと繋ぐ
            _isoScene.hostContainer = this;

            // 分かりにくいのでおなじみのグリッドも追加
            var grid:IsoGrid = new IsoGrid();
            grid.cellSize = 16;
            _isoScene.addChild( grid );
            
            /*
            * 外部画像をロードしてセット
            */
            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener( Event.COMPLETE, _setImage );
            loader.load( new URLRequest( "./images/mario.gif" ) );

            function _setImage( e:Event ):void
            {
                // ロードした画像をセット
                var marioImage:Bitmap = e.target.content;
                // マリオの足下がグリッドの座標と一致するように修正
                marioImage.x = -marioImage.width/2;
                marioImage.y = -marioImage.height;

                var isoSprite:IsoSprite = new IsoSprite();
                // spritesに表示したい画像をセット
                isoSprite.sprites = [ marioImage ];
                isoSprite.x = 16;
                isoSprite.y = 16;
                // シーンに追加
                _isoScene.addChild( isoSprite );

                // レンダリング
                _isoScene.render();
            }
        }
    }
}
あたごんとFlash-まりおさん表示

IsoSpriteのspritesに画像を配列で指定するだけです。とてもシンプル。
DisplayObjectであればMovieClipでも何でもOKです。
 
例えば、次の3つの画像とマリオさんを使うと、こんなものが作れちゃいます。

あたごんとFlash-床ブロック あたごんとFlash-?ブロック あたごんとFlash-普通のブロック あたごんとFlash-まりお

あたごんとFlash-gridmario
クリックするとマリオさんが動くようになっているのですが、Flashを貼り付けられないので、実際に動くサンプルが見たい方は、「コチラ 」の同じ内容のブログをご覧になって下さい。




少し長くなりますが、ソースはこんな感じです。マリオ移動のために、有名なGTween というTweenライブラリを利用しています。

package
{
    import as3isolib.core.ClassFactory;
    import as3isolib.core.IsoDisplayObject;
    import as3isolib.display.IsoSprite;
    import as3isolib.display.renderers.DefaultShadowRenderer;
    import as3isolib.display.scene.IsoGrid;
    import as3isolib.display.scene.IsoScene;
    import as3isolib.geom.IsoMath;
    import as3isolib.geom.Pt;
    
    import com.gskinner.motion.GTween;
    import com.gskinner.motion.GTweener;
    
    import eDpLib.events.ProxyEvent;
    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    
    public class IsoSpriteSample2 extends Sprite
    {
        private const GRID_SIZE:int = 16;
        private var _mario:IsoSprite;
        private var _isoScene:IsoScene;
        
        public function IsoSpriteSample2()
        {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;

            // シーンを作る
            _isoScene = new IsoScene();
            // displayListと繋ぐ
            _isoScene.hostContainer = this;
            // 見切れちゃうので、全体の表示位置をちょっとずらず
            x = 120;
            y = 50;
            
            _mario = new IsoSprite();
            /*
             * Flashで作ってswcに入れたMovieClipをセット
             */
            // マリオさん
            var marioImage:MovieClip = new Mario();
            marioImage.y = GRID_SIZE/2;
            _mario.sprites = [ marioImage ];
            _mario.width = GRID_SIZE;
            _mario.length = GRID_SIZE;
            _mario.height = GRID_SIZE*2;
            _mario.x = GRID_SIZE*3;
            _mario.y = GRID_SIZE*3;
            _mario.z = GRID_SIZE*1;
            _isoScene.addChild( _mario );
            
            /*
             * 5x5の床を敷き詰める
             */
            for ( var i:int = 0; i < 7; i++ )
            {
                for ( var j:int = 0; j < 7; j++ )
                {
                    var isoGroundBlock:IsoSprite = _createBlock( "groundBlock", GRID_SIZE*i, GRID_SIZE*j, 0 );
                    _isoScene.addChild( isoGroundBlock );
                }
            }
            
            /*
             * 1-1のブロック配置を再現
             */
            var blockBitmapData:BitmapData = new Block(0,0);
            var questionBlockBitmapData:BitmapData = new QuestionBlock(0,0);

            // ブロック1
            var isoBlock1:IsoSprite = _createBlock( "block", GRID_SIZE*1, GRID_SIZE*3, GRID_SIZE*4 );
            _isoScene.addChild( isoBlock1 );
            
            // ?ブロック1
            var isoQuestionBlock1:IsoSprite = _createBlock( "questionBlock", GRID_SIZE*2, GRID_SIZE*3, GRID_SIZE*4 );
            _isoScene.addChild( isoQuestionBlock1 );
            
            // ブロック2
            var isoBlock2:IsoSprite = _createBlock( "block", GRID_SIZE*3, GRID_SIZE*3, GRID_SIZE*4 );
            _isoScene.addChild( isoBlock2 );

            // ?ブロック2
            var isoQuestionBlock2:IsoSprite = _createBlock( "questionBlock", GRID_SIZE*4, GRID_SIZE*3, GRID_SIZE*4 );
            _isoScene.addChild( isoQuestionBlock2 );

            // ブロック3
            var isoBlock3:IsoSprite = _createBlock( "block", GRID_SIZE*5, GRID_SIZE*3, GRID_SIZE*4 );
            _isoScene.addChild( isoBlock3 );
            
            // 表示
            _isoScene.render();
            
            // クリックしたところへマリオを移動させる
            addEventListener( MouseEvent.CLICK, _moveMario );
        }
        
        private function _createBlock( type:String, x:int, y:int, z:int ):IsoSprite
        {
            var image:BitmapData;
            switch ( type )
            {
                case "block":
                    image = new Block( 0, 0 );
                    break;
                case "questionBlock":
                    image = new QuestionBlock( 0, 0 );
                    break;
                case "groundBlock":
                    image = new GroundBlock( 0, 0 );
                    break;
            }

            // 画像を作って、グリッドに合わせて真ん中にする
            var bitmap:Bitmap = new Bitmap( image );
            bitmap.x = -bitmap.width/2;
            bitmap.y = -bitmap.height/2;

            var isoBlock:IsoSprite = new IsoSprite();
            isoBlock.width = GRID_SIZE;
            isoBlock.length = GRID_SIZE;
            isoBlock.height = GRID_SIZE;
            isoBlock.sprites = [ bitmap ];
            isoBlock.x = x;
            isoBlock.y = y;
            isoBlock.z = z;
            
            return isoBlock;
        }

        private function _moveMario( e:MouseEvent ):void
        {
            // 見切れるのを直した分を考慮して、クリック座標を計算する
            var clickPos:Point = new Point( e.stageX-120, e.stageY-50 );
            // 目的地の極座標を、グリッド座標に変換する
            var destPt:Pt = new Pt( clickPos.x, clickPos.y+GRID_SIZE/2 );
            IsoMath.screenToIso( destPt );
            
            // フィールドの外をクリックした場合は無視する
            if ( destPt.x > GRID_SIZE * 7 || destPt.y > GRID_SIZE * 7 )
            {
                return;
            }
            
            var distance:Number = Pt.distance( new Pt( _mario.x, _mario.y ), destPt );
            var mario:MovieClip = _mario.sprites[ 0 ];
            mario.gotoAndPlay( "run" );

            // マリオのグリッド座標を、極座標に変換する
            var marioPt:Pt = new Pt( _mario.x, _mario.y );
            IsoMath.isoToScreen( marioPt );
            mario.scaleX = (clickPos.x - marioPt.x)/Math.abs(clickPos.x - marioPt.x);

            // 実行済みのtweenをキャンセルする
            GTweener.removeTweens( _mario );
            // 目的地までマリオをtweenさせる
            GTweener.to( _mario, distance/(GRID_SIZE*6), { "x":destPt.x, "y":destPt.y }, { onChange:_render, onComplete:_complete } );
        }

        private function _render( tween:GTween ):void
        {
            _isoScene.render();
        }
        
        private function _complete( tween:GTween ):void
        {
            var mario:MovieClip = _mario.sprites[ 0 ];
            mario.gotoAndPlay( "stop" );
        }
    }
}

なんとなく、アメーバぴぐっぽいものが作れそうな雰囲気がでてきました。ね?
 
 
以上で、IsoSpriteの説明は終了です!
次回は、フィルターについて書いてみたいと思います。