In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Wonderfl Fantasy Code Chronicle

FFCC的な3Dアクション
・を作るためのテンプレートみたいなもの

操作方法
・十字キー:移動
・SPACE or Z:ジャンプ
 ・キーボードによっては「左と上を押しながらSPACEを押してもジャンプしない」などの現象が起こります
/**
 * Copyright o_healer ( http://wonderfl.net/user/o_healer )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/5cfY
 */

// forked from osamX's ワンダフルクエスト
// forked from clockmaker's [Alternativa3D] Wonderfl Quest 3D
// forked from o_healer's 簡易2Dアクションゲームテンプレート

/*
 「Wonderfl Fantasy Code Chronicle」
 ・FFCC的な「ドット絵3Dアクション」

 操作方法
 ・十字キー:移動
 ・SPACE or Z:ジャンプ
  ・キーボードによっては「左と上を押しながらSPACEを押してもジャンプしない」などの現象が起こります

 アルゴリズム
 ・表示まわりは「Wonderfl Quest 3D」を少し拡張しただけ
 ・移動まわりは「簡易2Dアクションゲームテンプレート」を3次元に拡張したもの

 その他
 ・もう少しだけ高速化できそうだけど、対応は保留中。
  ・並んでるパネルを一体化すればもう少しだけ早くなるはず
*/


package {
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    import flash.ui.Keyboard;
    import flash.filters.*;
    import alternativ5.engine3d.materials.*;
    import alternativ5.engine3d.primitives.*;
    import alternativ5.types.Texture;
    import alternativ5.utils.MathUtils;

    [SWF(width="465", height="465", frameRate="60", backgroundColor="0x000000")]
    public class GameMain extends Sprite{
        //==Const==

        //マップ要素
        static private var MapIndexIter:int = 0;
        static public const O:int = MapIndexIter++;//0:空
        static public const G:int = MapIndexIter++;//Grass:芝生
        static public const S:int = MapIndexIter++;//Sand:砂
        static public const T:int = MapIndexIter++;//Tile:石畳
        static public const F:int = MapIndexIter++;//Flooring:フローリング
        static public const MAP_INDEX_NUM:int = MapIndexIter;

        //マップ
        static public const MAP_DATA:Array = [
            //1F
            [
                [G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, T, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, T, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
                [G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G, G],
            ],
            //2F
            [
                [T, T, T, T, T, O, O, O, O, O, O, O, O, O, O, O, O],
                [T, T, T, T, T, O, O, O, O, O, O, O, O, O, O, O, O],
                [T, T, T, T, T, O, O, O, O, O, O, O, O, O, O, T, O],
                [T, T, T, T, T, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
            ],
            //3F
            [
                [O, T, T, T, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, T, T, T, O, O, O, O, O, O, O, O, O, O, O, T, O],
                [O, T, T, T, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
            ],
            //4F
            [
                [O, O, T, T, T, T, T, O, O, O, O, O, O, O, O, O, O],
                [O, O, T, T, T, T, T, O, O, O, O, T, T, O, T, O, O],
                [O, O, O, O, O, O, T, O, O, O, O, T, T, O, O, O, O],
                [O, O, O, O, O, O, T, O, O, O, O, T, T, O, O, O, O],
                [O, O, O, O, O, O, T, T, T, O, O, T, T, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
                [O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O],
            ],
        ];

        //マップの長さ
        static public const MAP_NUM_X:int = MAP_DATA[0][0].length;
        static public const MAP_NUM_Y:int = MAP_DATA[0].length;
        static public const MAP_NUM_Z:int = MAP_DATA.length;


        //1パネルの長さ
        static public const PANEL_SIZE:int = 48;

        //全体の拡大率
        static public const SCALE:Number = 3;


        //画像URL:フィールド
        static public const FIELD_IMG_URLS:Array = [
            {index:G, url:"http://flash-scope.com/wonderfl/WonderflQuest/map/map0.png"},//芝生
            {index:S, url:"http://flash-scope.com/wonderfl/WonderflQuest/map/map1.png"},//砂
            {index:T, url:"http://flash-scope.com/wonderfl/WonderflQuest/map/map2.png"},//石畳
            {index:F, url:"http://flash-scope.com/wonderfl/WonderflQuest/map/map3.png"},//フローリング
        ];

        //画像URL:プレイヤー
        static public const PLAYER_IMG_URLS:Array = [
            "http://flash-scope.com/wonderfl/WonderflQuest/yuusha/yuushaF1.png", //前向き1
            "http://flash-scope.com/wonderfl/WonderflQuest/yuusha/yuushaF2.png", //前向き2
            "http://flash-scope.com/wonderfl/WonderflQuest/yuusha/yuushaB1.png", //後ろ向き1
            "http://flash-scope.com/wonderfl/WonderflQuest/yuusha/yuushaB2.png", //後ろ向き2
            "http://flash-scope.com/wonderfl/WonderflQuest/yuusha/yuushaL1.png", //左向き1
            "http://flash-scope.com/wonderfl/WonderflQuest/yuusha/yuushaL2.png", //左向き2
            "http://flash-scope.com/wonderfl/WonderflQuest/yuusha/yuushaR1.png", //右向き1
            "http://flash-scope.com/wonderfl/WonderflQuest/yuusha/yuushaR2.png" //右向き2
        ];

        //画像URL:空
        static public const SKY_URL:String = "http://clockmaker.jp/labs/100109_wonderfl_quest/imgs/sky.png";

        //コリジョン:1パネルを何分割するか
        static public const COLLISION_SCALE:Number = 16;
        //そのうちいくつをパネルからはみ出して採用するか
        static public const COLLISION_OFFSET:Number = 4;

        //コリジョン判定用
        static public const COLLISION_COLOR_EMPTY:uint = 0x000000;
        static public const COLLISION_COLOR_BLOCK:uint = 0xFFFFFF;


        //Utility
        static public const POS_ZERO:Point = new Point(0,0);


        //==Var==

        //【似非】Singleton【ていうかGlobalアクセス用】
        static public var Instance:GameMain;

        //3次元表示
        public var m_World:BasicTemplate;

        //コリジョン処理用Bitmap
        public var m_Collision:Array;

        //画像:フィールド
        public var m_BitmapData_Field:Array = new Array(MAP_INDEX_NUM);

        //画像:プレイヤー
        public var m_BitmapData_Player:Array = new Array(PLAYER_IMG_URLS.length);

        //画像:空
        public var m_BitmapData_Sky:BitmapData;

        //プレイヤー
        public var m_Player:Player;


        //==Function==


        //#Sequence

        //Init
        public function GameMain(){
            //Singleton
            Instance = this;

            //ロード開始
            Load();
        }

        //Load
        public function Load():void{
            var i:int;
            var num:int;

            var count:int = 0;

            var IncCount:Function = function():void{
                count++;
            };
            var DecCount:Function = function():void{
                count--;
                if(count <= 0){//全てのロードが完了したら
                    Reset();//初期化に移る
                }
            };

            //Load:Field
            {
                //Init
                //→Load時に行う。ロードされなかったものは空白として扱うため。

                //Load
                {
                    FIELD_IMG_URLS.forEach(function(item:Object, index:int, arr:Array):void{
                        var map_index:int = item.index;
                        var url:String = item.url;

                        IncCount();

                        var ldr:Loader = new Loader();
                        ldr.load(new URLRequest(url), new LoaderContext(true));
                        ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
                            //画像保存
                            {
/*
                                m_BitmapData_Field[map_index] = new BitmapData(PANEL_SIZE, PANEL_SIZE, true, 0x00000000);
                                m_BitmapData_Field[map_index].draw(ldr.content);
/*/
                                //最初から拡大して保存しておく
                                m_BitmapData_Field[map_index] = new BitmapData(PANEL_SIZE, PANEL_SIZE, true, 0x00000000);
                                const mtx:Matrix = new Matrix(SCALE,0,0,SCALE, 0,0);
                                m_BitmapData_Field[map_index].draw(ldr.content, mtx);
//*/
                            }

                            //残ロード数監視
                            {
                                DecCount();
                            }
                        });
                    });
                }
            }

            //Load:Player
            {
                //Init
                {
                    num = m_BitmapData_Player.length;
                    for(i = 0; i < num; i++){
                        m_BitmapData_Player[i] = new BitmapData(PANEL_SIZE, PANEL_SIZE, true, 0x00000000)
                    }
                }

                //Load
                {
                    PLAYER_IMG_URLS.forEach(function(item:Object, index:int, arr:Array):void{
                        var url:String = item as String;

                        IncCount();

                        var ldr:Loader = new Loader();
                        ldr.load(new URLRequest(url), new LoaderContext(true));
                        ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
                            //画像保存
                            {
                                m_BitmapData_Player[index].draw(ldr.content);
                            }

                            //残ロード数監視
                            {
                                DecCount();
                            }
                        });
                    });
                }
            }

            //Load:Sky
            {
                //Init
                {
                    m_BitmapData_Sky = new BitmapData(465, 465, true, 0x00000000)
                    addChild(new Bitmap(m_BitmapData_Sky));
                }

                //Load
                {
                    var url:String = SKY_URL;

                    IncCount();

                    var ldr:Loader = new Loader();
                    ldr.load(new URLRequest(url), new LoaderContext(true));
                    ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {
                        //画像保存
                        {
                            m_BitmapData_Sky.draw(ldr.content);
                        }

                        //残ロード数監視
                        {
                            DecCount();
                        }
                    });
                }
            }
        }

        //Reset
        public function Reset():void{
            var x:int;
            var y:int;
            var z:int;

            //3D
            {
                //Create
                {
                    if(m_World == null){
                        m_World = new BasicTemplate(465, 465, true);
                        addChild(m_World);
                    }
                }

                //Param
                {
                    m_World.camera.rotationY = Math.PI;
                    m_World.camera.rotationX = -Math.PI * 100/180;
                    m_World.camera.rotationZ = Math.PI;

//                    m_World.camera.x = yuushaPos.x + FLDSIZE / 2;
//                    m_World.camera.y = yuushaPos.y + FLDSIZE + 100;
                }
            }

            //Player
            {
                //Create
                {
                    if(m_Player == null){
                        m_Player = new Player();
                        m_World.scene.root.addChild(m_Player);
                    }
                }
//*
                //Param
                {
                    m_Player.x = PANEL_SIZE * MAP_NUM_X / 2;
                    m_Player.y = PANEL_SIZE * (MAP_NUM_Y-1);
                    m_Player.z = -100;
                }
//*/
                //Key
                {
                    stage.addEventListener(KeyboardEvent.KEY_DOWN, m_Player.onKeyDown);
                    stage.addEventListener(KeyboardEvent.KEY_UP, m_Player.onKeyUp);
                }
            }

            //Field
            {
                //
                const IsTwoSided:Boolean = false;

                //1Fの地面だけは一体化
                var ground:Plane;

                //地面
                ground = new Plane(PANEL_SIZE*MAP_NUM_X, PANEL_SIZE*MAP_NUM_Y, 1, 1, IsTwoSided);
                ground.rotationY = Math.PI;
                ground.x = PANEL_SIZE * MAP_NUM_X/2;
                ground.y = PANEL_SIZE * MAP_NUM_Y/2;
                ground.z = -PANEL_SIZE/2;

                //地面の画像はこれに描く
                var bmd_ground:BitmapData = new BitmapData(PANEL_SIZE*MAP_NUM_X, PANEL_SIZE*MAP_NUM_Y, true, 0x00000000);
                var mtx:Matrix = new Matrix(1,0,0,1, 0,0);

                //面を作るかのチェックのための関数(そちらにブロックがあるか)
                const IsEmpty:Function = function(in_X:int, in_Y:int, in_Z:int):Boolean{
                    //範囲チェック
                    {
                        if(in_X < 0){return true;}
                        if(in_X >= MAP_NUM_X){return true;}
                        if(in_Y < 0){return true;}
                        if(in_Y >= MAP_NUM_Y){return true;}
                        if(in_Z < 0){return true;}
                        if(in_Z >= MAP_NUM_Z){return true;}
                    }

                    //空白かどうか
                    return (MAP_DATA[in_Z][in_Y][in_X] == O);
                };

                //陰の実現のためのFilterを作る関数
                const CreateDarkFilter:Function = function(in_Ratio:Number):ColorMatrixFilter{
                    var r:Number = in_Ratio;
                    return new ColorMatrixFilter([
                        r, 0, 0, 0, 0,
                        0, r, 0, 0, 0,
                        0, 0, r, 0, 0,
                        0, 0, 0, 1, 0
                    ]);
                };

                var tex:Texture;
                var mat:TextureMaterial;

                for(z = 0; z < MAP_NUM_Z; z++){
                    for(y = 0; y < MAP_NUM_Y; y++){
                        for(x = 0; x < MAP_NUM_X; x++){
                            var bmd_ori:BitmapData = m_BitmapData_Field[MAP_DATA[z][y][x]];

                            //Check
                            if(bmd_ori == null){continue;}
                            var bmd:BitmapData = bmd_ori;

                            var fieldPlane:Plane;

                            if(z == 0){
                                //1Fの地面はひとまとめ
                                mtx.tx = (MAP_NUM_X-1-x)*PANEL_SIZE;
                                mtx.ty = (MAP_NUM_Y-1-y)*PANEL_SIZE;
                                bmd_ground.draw(bmd, mtx);
                            }else{
                                //2Fより上は個別対応

                                //上面
                                if(IsEmpty(x,y,z+1)){
                                    fieldPlane = new Plane(bmd.width, bmd.height, 1, 1, IsTwoSided);
                                    fieldPlane.rotationY = Math.PI;
                                    fieldPlane.x = PANEL_SIZE *  x + PANEL_SIZE/2;
                                    fieldPlane.y = PANEL_SIZE *  y + PANEL_SIZE/2;
                                    fieldPlane.z = PANEL_SIZE * -z - PANEL_SIZE/2;

                                    tex = new Texture(bmd);
                                    mat = new TextureMaterial(tex);
                                    fieldPlane.cloneMaterialToAllSurfaces(mat);

                                    m_World.scene.root.addChild(fieldPlane);
                                }
                                //下面
                                if(IsEmpty(x,y,z-1)){
                                    bmd = bmd_ori.clone();

                                    fieldPlane = new Plane(bmd.width, bmd.height, 1, 1, IsTwoSided);
                                    fieldPlane.rotationY = 0;
                                    fieldPlane.x = PANEL_SIZE *  x + PANEL_SIZE/2;
                                    fieldPlane.y = PANEL_SIZE *  y + PANEL_SIZE/2;
                                    fieldPlane.z = PANEL_SIZE * -z + PANEL_SIZE/2;

                                    //暗くする
                                    const filter_bottom:ColorMatrixFilter = CreateDarkFilter(0.2);
                                    bmd.applyFilter(bmd, bmd.rect, POS_ZERO, filter_bottom);

                                    tex = new Texture(bmd);
                                    mat = new TextureMaterial(tex);
                                    fieldPlane.cloneMaterialToAllSurfaces(mat);

                                    m_World.scene.root.addChild(fieldPlane);
                                }
                                //正面
                                if(IsEmpty(x,y+1,z)){
                                    bmd = bmd_ori.clone();

                                    fieldPlane = new Plane(bmd.width, bmd.height, 1, 1, IsTwoSided);
                                    fieldPlane.rotationX = -Math.PI/2;
                                    fieldPlane.x = PANEL_SIZE *  x + PANEL_SIZE/2;
                                    fieldPlane.y = PANEL_SIZE *  y + PANEL_SIZE;
                                    fieldPlane.z = PANEL_SIZE * -z;

                                    //暗くする
                                    const filter_front:ColorMatrixFilter = CreateDarkFilter(0.6);
                                    bmd.applyFilter(bmd, bmd.rect, POS_ZERO, filter_front);

                                    tex = new Texture(bmd);
                                    mat = new TextureMaterial(tex);
                                    fieldPlane.cloneMaterialToAllSurfaces(mat);

                                    m_World.scene.root.addChild(fieldPlane);
                                }
                                //左面
                                if(IsEmpty(x+1,y,z)){
                                    bmd = bmd_ori.clone();

                                    fieldPlane = new Plane(bmd.width, bmd.height, 1, 1, IsTwoSided);
                                    fieldPlane.rotationY = Math.PI/2;
                                    fieldPlane.x = PANEL_SIZE *  x + PANEL_SIZE;
                                    fieldPlane.y = PANEL_SIZE *  y + PANEL_SIZE/2;
                                    fieldPlane.z = PANEL_SIZE * -z;

                                    //暗くする
                                    const filter_left:ColorMatrixFilter = CreateDarkFilter(0.4);
                                    bmd.applyFilter(bmd, bmd.rect, POS_ZERO, filter_left);

                                    tex = new Texture(bmd);
                                    mat = new TextureMaterial(tex);
                                    fieldPlane.cloneMaterialToAllSurfaces(mat);

                                    m_World.scene.root.addChild(fieldPlane);
                                }
                                //右面
                                if(IsEmpty(x-1,y,z)){
                                    bmd = bmd_ori.clone();

                                    fieldPlane = new Plane(bmd.width, bmd.height, 1, 1, IsTwoSided);
                                    fieldPlane.rotationY = -Math.PI/2;
                                    fieldPlane.x = PANEL_SIZE *  x;
                                    fieldPlane.y = PANEL_SIZE *  y + PANEL_SIZE/2;
                                    fieldPlane.z = PANEL_SIZE * -z;

                                    //暗くする
                                    const filter_right:ColorMatrixFilter = CreateDarkFilter(0.8);
                                    bmd.applyFilter(bmd, bmd.rect, POS_ZERO, filter_right);

                                    tex = new Texture(bmd);
                                    mat = new TextureMaterial(tex);
                                    fieldPlane.cloneMaterialToAllSurfaces(mat);

                                    m_World.scene.root.addChild(fieldPlane);
                                }
                                //裏は省略(カメラの回転があるようなら描く)
                            }
                        }
                    }
                }

                //地面画像設定
                var tex_ground:Texture = new Texture(bmd_ground);
                var mat_ground:TextureMaterial = new TextureMaterial(tex_ground);
                ground.cloneMaterialToAllSurfaces(mat_ground);
                //地面登録
                m_World.scene.root.addChild(ground);
            }

            //Collision
            {
                if(m_Collision == null){
                    m_Collision = new Array(MAP_NUM_Z);

                    var rect:Rectangle = new Rectangle(-COLLISION_OFFSET,-COLLISION_OFFSET, COLLISION_SCALE+2*COLLISION_OFFSET,COLLISION_SCALE+2*COLLISION_OFFSET);

                    for(z = 0; z < MAP_NUM_Z; z++){
                        m_Collision[z] = new BitmapData(COLLISION_SCALE * MAP_NUM_X, COLLISION_SCALE * MAP_NUM_Y, false, COLLISION_COLOR_EMPTY);

                        for(y = 0; y < MAP_NUM_Y; y++){
                            rect.y = -COLLISION_OFFSET + y*COLLISION_SCALE;
                            for(x = 0; x < MAP_NUM_X; x++){
                                rect.x = -COLLISION_OFFSET + x*COLLISION_SCALE;
                                if(m_BitmapData_Field[MAP_DATA[z][y][x]]){//画像があればコリジョンもあるとみなす
                                    m_Collision[z].fillRect(rect, COLLISION_COLOR_BLOCK);
                                }
                            }
                        }
                    }
                }
            }

            //Update
            {
                //!!多重登録を防止したい
                addEventListener(Event.ENTER_FRAME, Update);
            }
        }

        //Update
        public function Update(e:Event = null):void{
            //Player
            {
                m_Player.Update();
            }

            //Camera
            {
                m_World.camera.x = m_Player.x;
                m_World.camera.y = m_Player.y + PANEL_SIZE + 100;
                //m_World.camera.z = m_Player.z - 50;
                m_World.camera.z = MathUtils.Lerp(m_World.camera.z, m_Player.z - 50, 0.2);//補間処理を入れてみる
            }
        }


        //#

        //IsCollision
        public function IsCollision(in_X:int, in_Y:int, in_Z:int):Boolean{
/*
            //#MAP_DATAで判断するバージョン

            //配列にアクセスするためのIndex化
            var x:int = Math.floor( in_X / PANEL_SIZE);
            var y:int = Math.floor( in_Y / PANEL_SIZE);
            var z:int = Math.floor(-in_Z / PANEL_SIZE);

            //範囲チェック
            {
                var Flag_for_RangeOut:Boolean = true;//範囲外はコリジョンとみなす(通行不能)

                if(x < 0){return Flag_for_RangeOut;}
                if(x >= MAP_NUM_X){return Flag_for_RangeOut;}
                if(y < 0){return Flag_for_RangeOut;}
                if(y >= MAP_NUM_Y){return Flag_for_RangeOut;}
                if(z < 0){return Flag_for_RangeOut;}
                if(z >= MAP_NUM_Z){return false;}//天井だけは突き抜けてOK
            }

            //要素が空でなければコリジョンとみなす
            return (MAP_DATA[z][y][x] != O);
/*/
            //#Bitmapで判断するバージョン

            //BitmapにアクセスするためのIndex化
            var x:int = Math.floor( in_X * COLLISION_SCALE / PANEL_SIZE);
            var y:int = Math.floor( in_Y * COLLISION_SCALE / PANEL_SIZE);
            var z:int = Math.floor(-in_Z / PANEL_SIZE);//こっちはBitmapじゃないのでそのまま

            //範囲チェック
            {
                var Flag_for_RangeOut:Boolean = true;//範囲外はコリジョンとみなす(通行不能)

                if(x < 0){return Flag_for_RangeOut;}
                if(x >= MAP_NUM_X * COLLISION_SCALE){return Flag_for_RangeOut;}
                if(y < 0){return Flag_for_RangeOut;}
                if(y >= MAP_NUM_Y * COLLISION_SCALE){return Flag_for_RangeOut;}
                if(z < 0){return Flag_for_RangeOut;}
                if(z >= MAP_NUM_Z){return false;}//天井だけは突き抜けてOK
            }

            //
            return (m_Collision[z].getPixel(x, y) == COLLISION_COLOR_BLOCK);
//*/
        }
    }
}



import flash.display.*;
import flash.errors.*;
import flash.events.*;
import flash.geom.*;
import flash.net.URLRequest;
import flash.ui.Keyboard;
import flash.system.LoaderContext;
import alternativ5.engine3d.controllers.*;
import alternativ5.engine3d.core.*;
import alternativ5.engine3d.display.*;
import alternativ5.engine3d.materials.*;
import alternativ5.engine3d.primitives.*;



//#プレイヤー
class Player extends Plane{
    //==Const==

    //移動速度
    static public const MOVE_VEL:Number = 72.0;

    //ジャンプ速度
    static public const JUMP_VEL:Number = 130.0;

    //重力
    static public const GRAVITY:Number = 100.0;

    //入力
    static private var InputIter:int = 0;
    static public const INPUT_U:int        = InputIter++;
    static public const INPUT_D:int        = InputIter++;
    static public const INPUT_L:int        = InputIter++;
    static public const INPUT_R:int        = InputIter++;
    static public const INPUT_JUMP:int    = InputIter++;
    static public const INPUT_NUM:int    = InputIter;

    //向き
    static public const DIR_U:int = 0;
    static public const DIR_D:int = 1;
    static public const DIR_L:int = 2;
    static public const DIR_R:int = 3;

    //アニメーション時間(何秒で切り替えるか)
    static public const ANIM_INTERVAL:Number = 0.5;


    //==Var==

    //2次元での表示画像
    public var m_Graphic:MovieClip = new MovieClip();
    public var  m_GraphicList:Array;

    //入力
    public var m_Input:Array = new Array(INPUT_NUM);

    //速度
    public var m_Vel:Vector3D = new Vector3D(0,0,0);

    //接地フラグ
    public var m_GroundFlag:Boolean =false;

    //向き
    public var m_Dir:int = DIR_D;

    //アニメーション用タイマー
    public var m_AnimTimer:Number = 0;


    //==Function==

    //Init
    public function Player(){
        var i:int;

        //Super
        {
            const DivideNum:int = 1;
            super(GameMain.PANEL_SIZE, GameMain.PANEL_SIZE, DivideNum, DivideNum);
        }

        //Param
        {
            //垂直になるようにする
            this.rotationX = Math.PI/2;

            //m_Input
            for(i = 0; i < INPUT_NUM; i++){
                m_Input[i] = false;
            }
        }

        //Graphic
        {
            var num:int = 8;
            m_GraphicList = new Array(8);
            for(i = 0; i < num; i++){
                m_GraphicList[i] = new Bitmap(GameMain.Instance.m_BitmapData_Player[i]);
                m_Graphic.addChild(m_GraphicList[i]);
            }
        }

        //Attach Graphic
        {
            var ratio:Number = GameMain.PANEL_SIZE / GameMain.SCALE;
            cloneMaterialToAllSurfaces(new MovieClipMaterial(m_Graphic, ratio, ratio));
        }

/*
        //Key
        //→こいつではstageには触れないらしい
        {
            addEventListener(
                Event.ADDED_TO_STAGE,//stageを扱うので、登録されたときに実行する
                function(e:Event):void{
                    stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
                    stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
                }
            );
        }
//*/
    }

    //Update
    public function Update(e:Event=null):void{
        var DeltaTime:Number = 1/24.0;

        //Move
        Update_Move(DeltaTime);

        //Anim
        Update_Anim(DeltaTime);
    }

    //Update : Move
    public function Update_Move(in_DeltaTime:Number):void{
        //移動元
        var OldX:int;
        var OldY:int;
        var OldZ:int;
        {
            OldX = this.x;
            OldY = this.y;
            OldZ = this.z;
        }

        //移動先
        var NewX:int = OldX;
        var NewY:int = OldY;
        var NewZ:int = OldZ;
        {
            //m_Vel.x & m_Vel.y
            //XY方向の移動処理
            {
                //目標方向を入力から決定
                var TrgVel:Vector3D = new Vector3D(0,0,0);
                {//ついでに向きもここで変更
                    if(m_Input[INPUT_U]){TrgVel.y -= 1; m_Dir = DIR_U;}
                    if(m_Input[INPUT_D]){TrgVel.y += 1; m_Dir = DIR_D;}
                    if(m_Input[INPUT_L]){TrgVel.x -= 1; m_Dir = DIR_L;}
                    if(m_Input[INPUT_R]){TrgVel.x += 1; m_Dir = DIR_R;}
                }

                //補正
                if(TrgVel.length > 0.1){
                    //入力あり
                    //→指定方向に加速

                    //長さがMOVE_VELのベクトルに変更
                    TrgVel.normalize();
                    TrgVel.scaleBy(MOVE_VEL);
                }else{
                    //入力なし
                    //→減速

                    //そのまま使う
                }

                //適用
                var ratio:Number = 0.8;//0に近いほど慣性が働く。1ならば慣性なし。
                m_Vel.x = MathUtils.Lerp(m_Vel.x, TrgVel.x, ratio);
                m_Vel.y = MathUtils.Lerp(m_Vel.y, TrgVel.y, ratio);
            }

            //m_Vel.z
            {
                //ジャンプ処理
                {
                    if(m_Input[INPUT_JUMP] && m_GroundFlag){
                        m_Vel.z = -JUMP_VEL;
                    }

                    m_GroundFlag = false;
                }

                //重力処理
                {
                    m_Vel.z += GRAVITY * in_DeltaTime;
                }
            }

            //NewX, NewY, NewZ
            //目標地点の計算
            {
                NewX += m_Vel.x * in_DeltaTime;
                NewY += m_Vel.y * in_DeltaTime;
                NewZ += m_Vel.z * in_DeltaTime;
            }
        }

        //移動処理
        {
            var NextX:int = OldX;
            var NextY:int = OldY;
            var NextZ:int = OldZ;

            //Z
            while(NextZ != NewZ){
                //次の座標に移る
                if(NextZ < NewZ){
                    NextZ++;
                }else{
                    NextZ--;
                }

                //コリジョンチェック
                if(GameMain.Instance.IsCollision(NextX, NextY, NextZ)){
                    NextZ = this.z;//移動できなければ移動できたところまで戻して
                    m_Vel.z = 0;//速度を0にして
                    if(OldZ < NewZ){m_GroundFlag = true;}//(下方向への移動なら)接地したことにして
                    break;//終了
                }

                //コリジョンがなければ今の座標を採用
                this.z = NextZ;
            }

            //Y
            while(NextY != NewY){
                //次の座標に移る
                if(NextY < NewY){
                    NextY++;
                }else{
                    NextY--;
                }

                //コリジョンチェック
                if(GameMain.Instance.IsCollision(NextX, NextY, NextZ)){
                    NextY = this.y;//移動できなければ移動できたところまで戻して
                    m_Vel.y = 0;//速度を0にして
                    break;//終了
                }

                //コリジョンがなければ今の座標を採用
                this.y = NextY;
            }

            //X
            while(NextX != NewX){
                //次の座標に移る
                if(NextX < NewX){
                    NextX++;
                }else{
                    NextX--;
                }

                //コリジョンチェック
                if(GameMain.Instance.IsCollision(NextX, NextY, NextZ)){
                    NextX = this.x;//移動できなければ移動できたところまで戻して
                    m_Vel.x = 0;//速度を0にして
                    break;//終了
                }

                //コリジョンがなければ今の座標を採用
                this.x = NextX;
            }
        }
    }

    //Update : Anim
    public function Update_Anim(in_DeltaTime:Number):void{
        //Timer
        {
            m_AnimTimer += in_DeltaTime;
            if(ANIM_INTERVAL*2 <= m_AnimTimer){
                m_AnimTimer -= ANIM_INTERVAL*2;
            }
        }

        //Anim
        {
            //採用するIndex
            var anim_index:int;
            {
                //方向で基準Indexを設定
                switch(m_Dir){
                case DIR_U:
                    anim_index = 2;//後向き
                    break;
                case DIR_D:
                    anim_index = 0;//前向き
                    break;
                case DIR_L:
                    anim_index = 4;//左向き
                    break;
                case DIR_R:
                    anim_index = 6;//右向き
                    break;
                }

                //タイマーに応じてIndexにオフセットを適用
                if(ANIM_INTERVAL <= m_AnimTimer){
                    anim_index += 1;
                }
            }

            //採用するやつ以外は全て不可視にする
            for(var i:int = 0; i < 8; i++){
                m_GraphicList[i].visible = (i == anim_index);
            }
        }
    }

    //Input
    private function SetInput(in_KeyCode:uint, in_Flag:Boolean):void{
        switch (in_KeyCode){
        case Keyboard.UP:
            m_Input[INPUT_U] = in_Flag;
            break;
        case Keyboard.DOWN:
            m_Input[INPUT_D] = in_Flag;
            break;
        case Keyboard.LEFT:
            m_Input[INPUT_L] = in_Flag;
            break;
        case Keyboard.RIGHT:
            m_Input[INPUT_R] = in_Flag;
            break;
        case Keyboard.SPACE://SPACE
        case 90://Z
            m_Input[INPUT_JUMP] = in_Flag;
            break;
        }
    }
    public function onKeyDown(event:KeyboardEvent):void{
        SetInput(event.keyCode, true);
    }
    public function onKeyUp(event:KeyboardEvent):void{
        SetInput(event.keyCode, false);
    }
}

class BasicTemplate extends Sprite {

    /**
     * 新しい BasicTemplate インスタンスを作成します。
     * @param    viewWidth
     * @param    viewHeight
     * @param    scaleToStage
     */
    public function BasicTemplate(viewWidth:int = 640, viewHeight:int = 480, scaleToStage:Boolean = true) {
        _viewWidth = viewWidth;
        _viewHeight = viewHeight;
        _scaleToStage = scaleToStage;

        // Creating scene
        scene = new Scene3D();
        scene.splitAnalysis = false; // not analysis for performance
        scene.root = new Object3D();

        // Adding camera
        camera = new Camera3D();
        //camera.z = -1000;
        scene.root.addChild(camera);

        // camera contoller
        cameraContoller = new CameraController(this);
        cameraContoller.camera = camera;

        // set view
        view = new View();
        view.camera = camera;
        addChild(view);

        // stage
        if (stage)
            init();
        else
            addEventListener(Event.ADDED_TO_STAGE, init);
    }
    /**
     * カメラインスタンスです。
     */
    public var camera:Camera3D;
    /**
     * カメラコントローラーです。
     */
    public var cameraContoller:CameraController;
    /**
     * シーンインスタンスです。
     */
    public var scene:Scene3D;
    /**
     * ビューインスタンスです。
     */
    public var view:View;

    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング後に実行したい処理を記述します。
     */
    protected var _onPostRender:Function = function():void {};

    /**
     * 初期化されたときに実行されるイベントです。
     * 初期化時に実行したい処理を記述します。
     */
    private var _onInit:Function = function():void {};

    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング前に実行したい処理を記述します。
     */
    private var _onPreRender:Function = function():void {};
    private var _scaleToStage:Boolean;
    private var _viewHeight:int;

    private var _viewWidth:int;

    public function get onInit():Function {
        return _onInit;
    }

    public function set onInit(value:Function):void {
        _onInit = value;
    }

    public function get onPostRender():Function {
        return _onPostRender;
    }

    public function set onPostRender(value:Function):void {
        _onPostRender = value;
    }

    public function get onPreRender():Function {
        return _onPreRender;
    }

    public function set onPreRender(value:Function):void {
        _onPreRender = value;
    }

    /**
     * シングルレンダリング(レンダリングを一回だけ)を実行します。
     */
    public function singleRender():void {
        onRenderTick();
    }

    /**
     * レンダリングを開始します。
     */
    public function startRendering():void {
        addEventListener(Event.ENTER_FRAME, onRenderTick);
    }

    /**
     * レンダリングを停止します。
     */
    public function stopRendering():void {
        removeEventListener(Event.ENTER_FRAME, onRenderTick);
    }

    /**
     * 初期化されたときに実行されるイベントです。
     * 初期化時に実行したい処理をオーバーライドして記述します。
     */
    protected function atInit():void {
    }

    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング後に実行したい処理をオーバーライドして記述します。
     */
    protected function atPostRender():void {
    }

    /**
     * Event.ENTER_FRAME 時に実行されるレンダリングのイベントです。
     * レンダリング前に実行したい処理をオーバーライドして記述します。
     */
    protected function atPreRender():void {
    }

    /**
     * @private
     */
    private function init(e:Event = null):void {
        // resize
        stage.addEventListener(Event.RESIZE, onResize);
        onResize(null);

        // render
        startRendering();

        atInit();
        _onInit();

    }

    /**
     * @private
     */
    private function onRenderTick(e:Event = null):void {
        atPreRender();
        _onPreRender();
        scene.calculate();
        atPostRender();
        _onPostRender();
    }

    /**
     * @private
     */
    private function onResize(event:Event = null):void {
        if (_scaleToStage) {
            view.width = stage.stageWidth;
            view.height = stage.stageHeight;
        } else {
            view.width = _viewWidth;
            view.height = _viewHeight;
        }
    }
}


class MathUtils{
    //Lerp
    static public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
        return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
    }
}