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

シロクロ×ダイバー

「シロクロ×ダイバー」(正式版)
 ・概要
  ・「ブロック=スペース」のステージでゴールに辿り着くアクションゲーム
 ・操作方法(画面を2回クリックしないと反応しないかもしれない)
  ・←→:移動
  ・↑:ジャンプ
  ・R:リスタート
 ・Forkしてステージを作れます
  ・下の「MAP」の中身を変更するだけで、ステージを自作できます
   ・「Q:白ゲート」には対応してないけど

 アルゴリズム概要
 ・基本的には「白い部分」と「黒い部分」の「境界」をコリジョンにしているだけ
  ・「黒い部分にブラー」+「黒い部分を抜く」=「境界線」
  ・ゲートの部分はブラー時に「黒い部分」とみなさないことで「境界線」を作らないようにしている

 雑感
 ・地味に色々と集大成
  ・移動アルゴリズム=簡易移動アルゴリズムとBox2Dの統合版
  ・GraphicsもBitmapもfiltersもmaskも利用
  ・高速化:DirtyRect、事前計算
 ・以下の細かい問題は残っているけど、コスト的にひとまずここまで
  ・リスタート時に前回のコリジョンが残ってる
  ・ゴールからのリスタート時にプレイヤーの速度などが残ったまま
  ・ブロックに乗った時の挙動が不安定
  ・ブロックを2つ以上並べて押すと、2つが重なってしまう
  ・ブロックを押すときに(家のノートPCだと)処理落ちする


 ToDo(やる予定だったこと)
 ・ブロックが壁にぶつかったらBox2Dもそういう風に判定させる(めりこませないように)
  →Contactまわりをいじれば良さそうだが、ちょっとコストが高すぎる。Box2Dを使わずに自作するのと同じ程度のコスト
 ・ちゃんとしたゴール処理
  →諦めた
 ・白ブロックの作成
  ・抜きの対応
  →描画コストがさらにかさむので対応保留

 Next(次やるのなら)
 ・白ブロックの作成
  ・高速化も必要
 ・可変長ブロックの作成
  ・ブロックは正方形でなくても良く、さらに四角である必要もない
 ・移動アルゴリズムを完全に自前化
  ・箱を押す処理などを含め、全て自前にしてしまって良いかも
/**
 * 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/e5y9
 */

/*
 「シロクロ×ダイバー」(正式版)
 ・概要
  ・「ブロック=スペース」のステージでゴールに辿り着くアクションゲーム
 ・操作方法(画面を2回クリックしないと反応しないかもしれない)
  ・←→:移動
  ・↑:ジャンプ
  ・R:リスタート
 ・Forkしてステージを作れます
  ・下の「MAP」の中身を変更するだけで、ステージを自作できます
   ・「Q:白ゲート」には対応してないけど

 アルゴリズム概要
 ・基本的には「白い部分」と「黒い部分」の「境界」をコリジョンにしているだけ
  ・「黒い部分にブラー」+「黒い部分を抜く」=「境界線」
  ・ゲートの部分はブラー時に「黒い部分」とみなさないことで「境界線」を作らないようにしている

 雑感
 ・地味に色々と集大成
  ・移動アルゴリズム=簡易移動アルゴリズムとBox2Dの統合版
  ・GraphicsもBitmapもfiltersもmaskも利用
  ・高速化:DirtyRect、事前計算
 ・以下の細かい問題は残っているけど、コスト的にひとまずここまで
  ・リスタート時に前回のコリジョンが残ってる
  ・ゴールからのリスタート時にプレイヤーの速度などが残ったまま
  ・ブロックに乗った時の挙動が不安定
  ・ブロックを2つ以上並べて押すと、2つが重なってしまう
  ・ブロックを押すときに(家のノートPCだと)処理落ちする


 ToDo(やる予定だったこと)
 ・ブロックが壁にぶつかったらBox2Dもそういう風に判定させる(めりこませないように)
  →Contactまわりをいじれば良さそうだが、ちょっとコストが高すぎる。Box2Dを使わずに自作するのと同じ程度のコスト
 ・ちゃんとしたゴール処理
  →諦めた
 ・白ブロックの作成
  ・抜きの対応
  →描画コストがさらにかさむので対応保留

 Next(次やるのなら)
 ・白ブロックの作成
  ・高速化も必要
 ・可変長ブロックの作成
  ・ブロックは正方形でなくても良く、さらに四角である必要もない
 ・移動アルゴリズムを完全に自前化
  ・箱を押す処理などを含め、全て自前にしてしまって良いかも
*/



package {
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.filters.*;
    import flash.ui.Keyboard;

    import Box2D.Dynamics.*;
    import Box2D.Dynamics.Contacts.*;
    import Box2D.Collision.*;
    import Box2D.Collision.Shapes.*;
    import Box2D.Common.Math.*;

    public class GameMain extends Sprite{
        //==Const==

        static public const PANEL_LEN:int = 32;//16;

        //マップ
        static public const O:int = 0;
        static public const X:int = 1;
        static public const Q:int = 2;
        static public const V:int = 3;
        static public const P:int = 99;
        static public const G:int = 100;
        static public const B:int = 200;
        static public const MAP:Array = [
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, O, O, O, O, O, O, O, O, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, O, O, O, O, X, X, V, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, P, O, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, X, V, X, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, V, X, O, O, O, O, O, X, X, X, X, X, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, O, O, O, O, B, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, X, X, X, X, X, X, X, X, X, X, X],
            [X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, X, X, X, X, X, X, X, X, X, X, X],
            [X, X, X, X, X, X, O, O, O, O, O, O, O, O, O, O, B, O, O, X, X, X, X, X, X, X, X, X, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, B, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, X, X, X, X, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, X, X, X, X, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, X, X, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, X, V, X, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, X, X, X, X, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, O, O, O, O, O, O, O, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, V, O, O, O, O, O, V, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, V, O, O, O, O, O, V, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, V, O, O, O, O, O, V, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, O, X, V, O, O, O, O, O, V, X, O],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, V, O, O, O, O, O, V, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, V, O, O, G, O, O, V, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X],
            [X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X],
        ];


        //==Var==

        //マップの幅
        static public var H:int = 100;//あとでちゃんと更新
        static public var W:int = 100;//あとでちゃんと更新

        //レイヤー:ルート
        public var m_Layer_Root:Sprite = new Sprite();
        //- レイヤー:ゲートエフェクト(エフェクトの部分)
        public var  m_Layer_Gate:Sprite = new Sprite();
        //- レイヤー:地形
        public var  m_Layer_Terrain:Sprite = new Sprite();
        //- レイヤー:ゲート地形(地形の黒い部分)
        public var  m_Layer_GateAsBlock:Sprite = new Sprite();
        //- レイヤー:ブロック:黒
        public var  m_Layer_Block_Black:Sprite = new Sprite();
        //- レイヤー:ブロック:白
        public var  m_Layer_Block_White:Sprite = new Sprite();
        //- レイヤー:プレイヤー
        public var  m_Layer_Player:Sprite = new Sprite();

        //グラフィック
        //- グラフィック:ブロック
        public var  m_BitmapData_Terrain:BitmapData;
        //- グラフィック:ゲート
        public var  m_BitmapData_Gate:BitmapData;
        //- グラフィック:ゲート地形
        public var  m_BitmapData_GateAsBlock:BitmapData;

        //地形判定に使うBitmapData
        public var m_BitmapData_Collision:BitmapData;
        public var m_BitmapData_Collision_ForBlackBlock:BitmapData;
        public var m_BitmapData_Collision_ForWhiteBlock:BitmapData;

        //高速化用事前計算
        public var m_BitmapData_Pre_TerrainAsBlack:BitmapData;

        //汎用ビットマップ(地形の計算に利用する)
        public var m_BitmapData_Util_0:BitmapData;
        public var m_BitmapData_Util_1:BitmapData;

        //コリジョン描画用Dirtyフラグまわり
        public var m_DirtyFlag:Boolean = false;
        public var m_DirtyFlag_ForBlackBlock:Boolean = false;
        public var m_DirtyFlag_ForWhiteBlock:Boolean = false;
        public var m_RedrawRect:Rectangle = new Rectangle();
        public var m_RedrawPoint:Point = new Point();

        //#Physics
           public var m_PhysWorld:b2World;

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

        //プレイヤー表示切り替え用マスク
        public var m_ShapeMask_ForPlayer:Sprite = new Sprite();

        //==Function==

        //Static Global Access
        static private var m_Instance:GameMain;
        static public function Instance():GameMain{return m_Instance;}

        //#Init
        public function GameMain(){
            //Init Later (for Using "stage" etc.)
            addEventListener(Event.ADDED_TO_STAGE, Init);

            m_Instance = this;
        }
        
        public function Init(e:Event = null):void{
            var x:int, y:int;

            //Init Once Only
            {
                removeEventListener(Event.ADDED_TO_STAGE, Init);
            }

            //Common Param
            {
                W = MAP[0].length * PANEL_LEN;
                H = MAP.length * PANEL_LEN;
            }

            //Setup Box2D
            //m_PhysWorld
            {//座標系はスケーリングせず、物理エンジン中の位置とドットの位置は同じ値になるようにする
                //Create World
                {
                    var worldAABB:b2AABB;
                    {
                        worldAABB = new b2AABB();
                        worldAABB.lowerBound.Set(-W, -H);
                        worldAABB.upperBound.Set(2*W, 2*H);
                    }

                    var gravity:b2Vec2;
                    {
                        gravity = new b2Vec2(0, 600 / IGameObject.PHYS_SCALE);
                    }

                    m_PhysWorld = new b2World(worldAABB, gravity, true);
                }
            }

            //BG
            {
                addChild(new Bitmap(new BitmapData(W, H, false, 0xFFFFFF)));
                //addChild(new Bitmap(new BitmapData(W, H, false, 0x000000)));//確認用
            }

            //Layer
            {
                addChild(m_Layer_Root);
                {
                    m_Layer_Root.addChild(m_Layer_Gate);
                    m_Layer_Root.addChild(m_Layer_Terrain);
                    m_Layer_Root.addChild(m_Layer_GateAsBlock);
                    m_Layer_Root.addChild(m_Layer_Block_Black);
                    m_Layer_Root.addChild(m_Layer_Block_White);
                    m_Layer_Root.addChild(m_Layer_Player);

                    m_Layer_Player.addChild(m_ShapeMask_ForPlayer);
                }
            }

            //グラフィック:下準備
            {
                //m_BitmapData_Terrain
                {
                    m_BitmapData_Terrain = new BitmapData(W,H, true, 0x00000000);
                    m_Layer_Terrain.addChild(new Bitmap(m_BitmapData_Terrain));
                }

                //m_BitmapData_Gate
                {
                    m_BitmapData_Gate = new BitmapData(W,H, true, 0x00000000);
                    m_Layer_Gate.addChild(new Bitmap(m_BitmapData_Gate));
                }

                //m_BitmapData_GateAsBlock
                {
                    m_BitmapData_GateAsBlock = new BitmapData(W,H, true, 0x00000000);
                    m_Layer_GateAsBlock.addChild(new Bitmap(m_BitmapData_GateAsBlock));
                }
            }

            //コリジョン:下準備
            {
                //m_BitmapData_Collision
                {
                    m_BitmapData_Collision = new BitmapData(W,H, true, 0x00000000);
                    m_BitmapData_Collision_ForBlackBlock = new BitmapData(W,H, true, 0x00000000);
                    m_BitmapData_Collision_ForWhiteBlock = new BitmapData(W,H, true, 0x00000000);
                }
            }

            //MAPに基づく処理
            var PlayerX:int = 0;
            var PlayerY:int = 0;
            var GoalX:int = 0;
            var GoalY:int = 0;
            {
                var NumX:int = MAP[0].length;
                var NumY:int = MAP.length;

                var rect:Rectangle = new Rectangle(0,0, PANEL_LEN,PANEL_LEN);
                var mtx:Matrix = new Matrix();

                //ゲートエフェクト用
                var GateEffect:Sprite = new Sprite();
                {
                    var s:Shape;
                    var g:Graphics;

                    //X
                    {
                        s = new Shape();
                        GateEffect.addChild(s);

                        //Draw Rect
                        g = s.graphics;
                        g.lineStyle(0, 0x0000000, 0.0);
                        g.beginFill(0x000000, 1.0);
                        g.drawRect(0,0, PANEL_LEN,PANEL_LEN);
                        g.endFill();

                        //Draw Effect
                        s.filters = [new GlowFilter(0x0088FF, 1.0, PANEL_LEN,1)];
                    }

                    //Y
                    {
                        s = new Shape();
                        GateEffect.addChild(s);

                        //Draw Rect
                        g = s.graphics;
                        g.lineStyle(0, 0x0000000, 0.0);
                        g.beginFill(0x000000, 1.0);
                        g.drawRect(0,0, PANEL_LEN,PANEL_LEN);
                        g.endFill();

                        //Draw Effect
                        s.filters = [new GlowFilter(0x0088FF, 1.0, 1,PANEL_LEN)];
                    }
                }

                //プレイヤー表示のマスク用
                var mask_for_player:Graphics;
                {
                    var mask_shape:Shape = new Shape();
                    m_ShapeMask_ForPlayer.addChild(mask_shape);

                    mask_for_player = mask_shape.graphics;
                    mask_for_player.lineStyle(0, 0x000000, 0.0);
                    mask_for_player.beginFill(0x000000, 1.0);
                }

                //MAPの要素に応じた処理
                for(y = 0; y < NumY; y++){
                    rect.y = y * PANEL_LEN;
                    mtx.ty = rect.y;
                    for(x = 0; x < NumX; x++){
                        rect.x = x * PANEL_LEN;
                        mtx.tx = rect.x;

                        switch(MAP[y][x]){
                        case O://空間
                            break;
                        case X://ブロック
                            m_BitmapData_Terrain.fillRect(rect, 0xFF000000);
                            mask_for_player.drawRect(rect.x, rect.y, rect.width, rect.height);
                            break;
                        case V://隣接する空間と移動可能なブロック
                            m_BitmapData_GateAsBlock.fillRect(rect, 0xFF000000);
                            m_BitmapData_Gate.draw(GateEffect, mtx);
                            mask_for_player.drawRect(rect.x, rect.y, rect.width, rect.height);
                            break;

                        case P://プレイヤー位置(それ以外は空間扱い)
                            PlayerX = rect.x + PANEL_LEN/2;
                            PlayerY = rect.y + PANEL_LEN/2;
                            break
                        case G://ゴール位置(それ以外は空間扱い)
                            GoalX = rect.x + PANEL_LEN/2;
                            GoalY = rect.y + PANEL_LEN/2;
                            break
//*
                        case B://黒ブロック位置
                            {
                                m_Layer_Block_Black.addChild(
                                    new MovableBlock(m_PhysWorld, rect.x + PANEL_LEN/2, rect.y + PANEL_LEN/2)
                                );
                            }
                            break;
//*/
                        }
                    }
                }

                //後処理
                {
                    mask_for_player.endFill();
                }
            }

            //Player
            {
                m_Player = new Player(m_PhysWorld, PlayerX, PlayerY);
                m_Layer_Player.addChild(m_Player);
            }

            //Goal
            {
                m_Layer_Root.addChild(new Goal(GoalX, GoalY));
            }

            //Bitmap : 事前計算
            {
                //m_BitmapData_Pre_TerrainAsBlack
                {
                    m_BitmapData_Pre_TerrainAsBlack = new BitmapData(W, H, true, 0x00000000);

                    const mtx_identity:Matrix = new Matrix();
                    const ct_black:ColorTransform = new ColorTransform(0,0,0,1);
                    m_BitmapData_Pre_TerrainAsBlack.draw(m_Layer_Terrain, mtx_identity, ct_black, BlendMode.NORMAL, m_BitmapData_Pre_TerrainAsBlack.rect);
                }
            }

            //Bitmap : Util
            {
                m_BitmapData_Util_0 = new BitmapData(W, H, true, 0x00000000);
                m_BitmapData_Util_1 = new BitmapData(W, H, true, 0x00000000);
            }

            //Dirty
            {
                ResetDirty();
            }

            //コリジョンを実際に描画
            {
                m_DirtyFlag_ForBlackBlock = true;
                m_DirtyFlag_ForWhiteBlock = true;
                RedrawCollision(new Rectangle(0,0,W,H), new Point());
//                RedrawCollision(new Rectangle(2*PANEL_LEN,0,W-2*PANEL_LEN,H), new Point(2*PANEL_LEN));//test
            }

            //Call "Update"
            {
                addEventListener(Event.ENTER_FRAME, Update);
            }

            //Debug
//            m_Layer_Root.addChild(new Bitmap(m_BitmapData_Collision));//コリジョンの可視化
//            m_Layer_Root.addChild(new Bitmap(m_BitmapData_Collision_ForBlackBlock));//コリジョンの可視化
//            m_Layer_Root.addChild(new Bitmap(m_BitmapData_Collision_ForWhiteBlock));//コリジョンの可視化
        }

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

            //Redraw Collision
            if(m_DirtyFlag){
                //
                RedrawCollision(m_RedrawRect, m_RedrawPoint);

                //
                ResetDirty();
            }

            //Update Phys
            {
                const iteration:int = 2;

                m_PhysWorld.Step(DeltaTime, iteration);
            }

            //Camera
            {//プレイヤーが画面に収まるようにRootの位置を調整
                var CAMERA_W:int = stage.stageWidth;
                var CAMERA_H:int = stage.stageHeight;

                var trgX:Number = m_Player.x - CAMERA_W/2.0;
                var trgY:Number = m_Player.y - CAMERA_H/2.0;

                var MinX:Number = 0.0;
                var MaxX:Number = W - CAMERA_W;
                var MinY:Number = 0.0;
                var MaxY:Number = H - CAMERA_H;

                if(trgX > MaxX){
                    trgX = MaxX;
                }
                if(trgY > MaxY){
                    trgY = MaxY;
                }
                if(trgX < MinX){
                    trgX = MinX;
                }
                if(trgY < MinY){
                    trgY = MinY;
                }

                m_Layer_Root.x = -trgX;
                m_Layer_Root.y = -trgY;
            }
        }

        //地形コリジョン生成
        public function RedrawCollision(in_Rect:Rectangle, in_Pos:Point):void
        {
/*
            //Dirtyな部分(今は全体描画になってる)
            var rect:Rectangle = in_Rect;//m_Bitmap_Terrain.rect;
            var rect_wide:Rectangle = new Rectangle(in_Rect.x-PANEL_LEN, in_Rect.y-PANEL_LEN, in_Rect.width+2*PANEL_LEN, in_Rect.height+2*PANEL_LEN);
            if(rect_wide.left < 0){rect_wide.left = 0;}
            if(rect_wide.right > W){rect_wide.right = W;}
            if(rect_wide.top  < 0){rect_wide.top = 0;}
            if(rect_wide.bottom > H){rect_wide.bottom = H;}
            var pos:Point = in_Pos;//new Point(0,0);

            const mtx_identity:Matrix = new Matrix();

            //m_BitmapData_Collision
            {
                pos.x = rect_wide.x;
                pos.y = rect_wide.y;

                //Reset
                {//rectより一回り大きい範囲をクリアするだけでよいが、一応全てリセット
                    m_BitmapData_Util_0.fillRect(m_BitmapData_Util_0.rect, 0x00000000);
                    m_BitmapData_Util_1.fillRect(m_BitmapData_Util_1.rect, 0x00000000);
                }

                //まずは白と黒の境界線が描画されたものを用意する
                {
                    //強制的に黒く描画
                    const ct_black:ColorTransform = new ColorTransform(0,0,0,1);
                    m_BitmapData_Util_0.draw(m_Layer_Terrain, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);
                    m_BitmapData_Util_0.draw(m_Layer_Block_Black, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);

                    //白でブラー
                    const blur_white:GlowFilter = new GlowFilter(0xFFFFFF,1.0, 2,2, 255);//幅が1でOKなら1で。
                    m_BitmapData_Util_1.applyFilter(m_BitmapData_Util_0, rect_wide, pos, blur_white);

                    //ゲート部分を描き足す
                    m_BitmapData_Util_1.draw(m_Layer_GateAsBlock, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);

                    //Rの値をAにセット:白は白のまま、黒かった部分は透明に。
                    m_BitmapData_Util_0.copyChannel(m_BitmapData_Util_1, rect_wide, pos, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
                }

                //そしてその境界線を膨らませて、実際のコリジョン描画にする
                {
                    //横に伸ばす
//                    const blur_x:GlowFilter = new GlowFilter(0xFFFFFF,1.0, PANEL_LEN*3/5,1, 255);
                    const blur_x:GlowFilter = new GlowFilter(0x888888,1.0, PANEL_LEN*3/5,1, 255);
                    m_BitmapData_Util_1.applyFilter(m_BitmapData_Util_0, rect_wide, pos, blur_x);

                    //縦に伸ばす
//                    const blur_y:GlowFilter = new GlowFilter(0xFFFFFF,1.0, 1,PANEL_LEN*3/5, 255);
                    const blur_y:GlowFilter = new GlowFilter(0x888888,1.0, 1,PANEL_LEN*3/5, 255);
                    m_BitmapData_Util_0.applyFilter(m_BitmapData_Util_1, rect_wide, pos, blur_y);
                }

                pos.x = rect.x;
                pos.y = rect.y;

                //それを実際のコリジョンに移す
                {
                    m_BitmapData_Collision.copyPixels(m_BitmapData_Util_0, rect, pos);
                }
            }

            //m_BitmapData_Collision_ForBlackBlock
            if(m_DirtyFlag_ForBlackBlock)
            {
                pos.x = rect_wide.x;
                pos.y = rect_wide.y;

                //Reset
                {//rectより一回り大きい範囲をクリアするだけでよいが、一応全てリセット
                    m_BitmapData_Util_0.fillRect(m_BitmapData_Util_0.rect, 0x00000000);
                    m_BitmapData_Util_1.fillRect(m_BitmapData_Util_1.rect, 0x00000000);
                }

                //まずは白と黒の境界線が描画されたものを用意する
                {
                    //強制的に黒く描画
//                    const ct_black:ColorTransform = new ColorTransform(0,0,0,1);
                    m_BitmapData_Util_0.draw(m_Layer_Terrain, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);
                    m_BitmapData_Util_0.draw(m_Layer_GateAsBlock, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);

                    //白でブラー
//                    const blur_white:GlowFilter = new GlowFilter(0xFFFFFF,1.0, 2,2, 255);//幅が1でOKなら1で。
                    m_BitmapData_Util_1.applyFilter(m_BitmapData_Util_0, rect_wide, pos, blur_white);

                    //Rの値をAにセット:白は白のまま、黒かった部分は透明に。
                    m_BitmapData_Util_0.copyChannel(m_BitmapData_Util_1, rect_wide, pos, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
                }

                //そしてその境界線を膨らませて、実際のコリジョン描画にする
                {
                    //横に伸ばす
//                    const blur_x_for_block:GlowFilter = new GlowFilter(0xFFFFFF,1.0, PANEL_LEN-4,1, 255);
                    const blur_x_for_block:GlowFilter = new GlowFilter(0x888888,1.0, PANEL_LEN*3/4,1, 255);
                    m_BitmapData_Util_1.applyFilter(m_BitmapData_Util_0, rect_wide, pos, blur_x_for_block);

                    //縦に伸ばす
//                    const blur_y_for_block:GlowFilter = new GlowFilter(0xFFFFFF,1.0, 1,PANEL_LEN-4, 255);
                    const blur_y_for_block:GlowFilter = new GlowFilter(0x888888,1.0, 1,PANEL_LEN*3/4, 255);
                    m_BitmapData_Util_0.applyFilter(m_BitmapData_Util_1, rect_wide, pos, blur_y_for_block);
                }

                pos.x = rect.x;
                pos.y = rect.y;

                //それを実際のコリジョンに移す
                {
                    m_BitmapData_Collision_ForBlackBlock.copyPixels(m_BitmapData_Util_0, rect, pos);
                }
            }
/*/
            //上でコメントアウトした処理の高速化版
            //Dirtyな部分(今は全体描画になってる)
            var rect:Rectangle = in_Rect;//m_Bitmap_Terrain.rect;
            var rect_wide:Rectangle = new Rectangle(in_Rect.x-PANEL_LEN, in_Rect.y-PANEL_LEN, in_Rect.width+2*PANEL_LEN, in_Rect.height+2*PANEL_LEN);
            if(rect_wide.left < 0){rect_wide.left = 0;}
            if(rect_wide.right > W){rect_wide.right = W;}
            if(rect_wide.top  < 0){rect_wide.top = 0;}
            if(rect_wide.bottom > H){rect_wide.bottom = H;}
            var pos:Point = in_Pos;//new Point(0,0);

            const mtx_identity:Matrix = new Matrix();

            //m_BitmapData_Collision
            {
                pos.x = rect_wide.x;
                pos.y = rect_wide.y;

                //Reset
                {//rectより一回り大きい範囲をクリアするだけでよいが、一応全てリセット
//                    m_BitmapData_Util_0.fillRect(m_BitmapData_Util_0.rect, 0x00000000);
                    m_BitmapData_Util_0.copyPixels(m_BitmapData_Pre_TerrainAsBlack, rect_wide, pos);
                    //m_BitmapData_Util_1.fillRect(rect_wide, 0x00000000);
                }

                //まずは白と黒の境界線が描画されたものを用意する
                {
                    //強制的に黒く描画
                    const ct_black:ColorTransform = new ColorTransform(0,0,0,1);
//                    m_BitmapData_Util_0.draw(m_Layer_Terrain, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);
                    m_BitmapData_Util_0.draw(m_Layer_Block_Black, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);

                    //白でブラー
                    const blur_white:GlowFilter = new GlowFilter(0xFFFFFF,1.0, 2,2, 255);//幅が1でOKなら1で。
                    m_BitmapData_Util_1.applyFilter(m_BitmapData_Util_0, rect_wide, pos, blur_white);

                    //ゲート部分を描き足す
                    m_BitmapData_Util_1.draw(m_Layer_GateAsBlock, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);

                    //Rの値をAにセット:白は白のまま、黒かった部分は透明に。
                    m_BitmapData_Util_0.copyChannel(m_BitmapData_Util_1, rect_wide, pos, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
                }

                //そしてその境界線を膨らませて、実際のコリジョン描画にする
                {
                    //横に伸ばす
//                    const blur_x:GlowFilter = new GlowFilter(0xFFFFFF,1.0, PANEL_LEN*3/5,1, 255);
                    const blur_x:GlowFilter = new GlowFilter(0x888888,1.0, PANEL_LEN*3/5,1, 255);
                    m_BitmapData_Util_1.applyFilter(m_BitmapData_Util_0, rect_wide, pos, blur_x);

                    //縦に伸ばす
//                    const blur_y:GlowFilter = new GlowFilter(0xFFFFFF,1.0, 1,PANEL_LEN*3/5, 255);
                    const blur_y:GlowFilter = new GlowFilter(0x888888,1.0, 1,PANEL_LEN*3/5, 255);
//                    m_BitmapData_Util_0.applyFilter(m_BitmapData_Util_1, rect_wide, pos, blur_y);
                }

                pos.x = rect.x;
                pos.y = rect.y;

                //それを実際のコリジョンに移す
                {
//                    m_BitmapData_Collision.copyPixels(m_BitmapData_Util_0, rect, pos);
                    m_BitmapData_Collision.applyFilter(m_BitmapData_Util_1, rect, pos, blur_y);
                }
            }

            //m_BitmapData_Collision_ForBlackBlock
            if(m_DirtyFlag_ForBlackBlock)
            {
                pos.x = rect_wide.x;
                pos.y = rect_wide.y;

                //Reset
                {//rectより一回り大きい範囲をクリアするだけでよいが、一応全てリセット
//                    m_BitmapData_Util_0.fillRect(m_BitmapData_Util_0.rect, 0x00000000);
                    m_BitmapData_Util_0.copyPixels(m_BitmapData_Pre_TerrainAsBlack, rect_wide, pos);
//                    m_BitmapData_Util_1.fillRect(m_BitmapData_Util_1.rect, 0x00000000);
                }

                //まずは白と黒の境界線が描画されたものを用意する
                {
                    //強制的に黒く描画
//                    const ct_black:ColorTransform = new ColorTransform(0,0,0,1);
//                    m_BitmapData_Util_0.draw(m_Layer_Terrain, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);
                    m_BitmapData_Util_0.draw(m_Layer_GateAsBlock, mtx_identity, ct_black, BlendMode.NORMAL, rect_wide);

                    //白でブラー
//                    const blur_white:GlowFilter = new GlowFilter(0xFFFFFF,1.0, 2,2, 255);//幅が1でOKなら1で。
                    m_BitmapData_Util_1.applyFilter(m_BitmapData_Util_0, rect_wide, pos, blur_white);

                    //Rの値をAにセット:白は白のまま、黒かった部分は透明に。
                    m_BitmapData_Util_0.copyChannel(m_BitmapData_Util_1, rect_wide, pos, BitmapDataChannel.RED, BitmapDataChannel.ALPHA);
                }

                //そしてその境界線を膨らませて、実際のコリジョン描画にする
                {
                    //横に伸ばす
//                    const blur_x_for_block:GlowFilter = new GlowFilter(0xFFFFFF,1.0, PANEL_LEN-4,1, 255);
//                    const blur_x_for_block:GlowFilter = new GlowFilter(0x888888,1.0, PANEL_LEN*3/4+1,1, 255);
                    const blur_x_for_block:GlowFilter = new GlowFilter(0x888888,1.0, PANEL_LEN-4,1, 255);
                    m_BitmapData_Util_1.applyFilter(m_BitmapData_Util_0, rect_wide, pos, blur_x_for_block);

                    //縦に伸ばす
//                    const blur_y_for_block:GlowFilter = new GlowFilter(0xFFFFFF,1.0, 1,PANEL_LEN-4, 255);
//                    const blur_y_for_block:GlowFilter = new GlowFilter(0x888888,1.0, 1,PANEL_LEN*3/4+1, 255);
                    const blur_y_for_block:GlowFilter = new GlowFilter(0x888888,1.0, 1,PANEL_LEN-4, 255);
                    m_BitmapData_Util_0.applyFilter(m_BitmapData_Util_1, rect_wide, pos, blur_y_for_block);
                }

                pos.x = rect.x;
                pos.y = rect.y;

                //それを実際のコリジョンに移す
                {
                    m_BitmapData_Collision_ForBlackBlock.copyPixels(m_BitmapData_Util_0, rect, pos);
                }
            }
//*/
        }


        //Dirty部分のリセット
        public function ResetDirty():void{
            m_DirtyFlag = false;
            m_DirtyFlag_ForBlackBlock = false;
            m_DirtyFlag_ForWhiteBlock = false;

            m_RedrawRect.x = W;
            m_RedrawRect.width = 0;
            m_RedrawRect.y = H;
            m_RedrawRect.height = 0;

            m_RedrawPoint.x = m_RedrawRect.x;
            m_RedrawPoint.y = m_RedrawRect.y;
        }

        //Dirty部分の追加
        public function AddDirty(in_LX:int, in_RX:int, in_UY:int, in_DY:int, in_IsBlackBlock:Boolean):void{
            //m_DirtyFlag
            {
                m_DirtyFlag = true;

                if(in_IsBlackBlock){
                    m_DirtyFlag_ForWhiteBlock;//黒いブロックが動いたら、白いブロック用の地形を再描画
                }else{
                    m_DirtyFlag_ForBlackBlock;//上の逆
                }
            }

            //m_RedrawRect
            {
                if(in_LX < m_RedrawRect.left){
                    m_RedrawRect.left = in_LX;
                }
                if(m_RedrawRect.right < in_RX){
                    m_RedrawRect.right = in_RX;
                }
                if(in_UY < m_RedrawRect.top){
                    m_RedrawRect.top = in_UY;
                }
                if(m_RedrawRect.bottom < in_DY){
                    m_RedrawRect.bottom = in_DY;
                }
            }

            //m_RedrawPoint
            {
                m_RedrawPoint.x = m_RedrawRect.x;
                m_RedrawPoint.y = m_RedrawRect.y;
            }
        }


        //Collision
        public function IsCollision(i_X:int, i_Y:int):Boolean{
            //範囲外は空間とみなす
            {
                if(i_X < 0){return false;}
                if(i_X >= W){return false;}
                if(i_Y < 0){return false;}
                if(i_Y >= H){return false;}
            }

            //一定以上白ければ壁とみなす
            var color:uint = m_BitmapData_Collision.getPixel32(i_X, i_Y);
            var r:uint = (color >> 16) & 0xFF;
            return r > 0x02;//
        }

        public function IsCollision_ForBlackBlock(i_X:int, i_Y:int):Boolean{
            //範囲外は空間とみなす
            {
                if(i_X < 0){return false;}
                if(i_X >= W){return false;}
                if(i_Y < 0){return false;}
                if(i_Y >= H){return false;}
            }

            //一定以上白ければ壁とみなす
            var color:uint = m_BitmapData_Collision_ForBlackBlock.getPixel32(i_X, i_Y);
            var r:uint = (color >> 16) & 0xFF;
            return r > 0x02;//
        }

        public function IsCollision_ForWhiteBlock(i_X:int, i_Y:int):Boolean{
            //範囲外は空間とみなす
            {
                if(i_X < 0){return false;}
                if(i_X >= W){return false;}
                if(i_Y < 0){return false;}
                if(i_Y >= H){return false;}
            }

            //一定以上白ければ壁とみなす
            var color:uint = m_BitmapData_Collision_ForWhiteBlock.getPixel32(i_X, i_Y);
            var r:uint = (color >> 16) & 0xFF;
            return r > 0x02;//
        }
    }
}


import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.filters.*;
import flash.ui.Keyboard;

import Box2D.Dynamics.*;
import Box2D.Dynamics.Contacts.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Common.Math.*;


//*
internal class IGameObject extends Sprite{
    //コリジョンを利用しつつ、Bitmapを見た動きをするOBJ

    //==Const==

    //Box2Dに大きい値を入れると上手く機能しないので、小さくするためのスケーリング
    static public const PHYS_SCALE:Number = 10.0;

    static public const DRAG_RATIO_O:Number = 0.7;
    static public const DRAG_RATIO_W:Number = 0.5;


    //==Var==

    public var m_Body:b2Body;

    //#StartPos
    public var m_StartPosX:int = 0;
    public var m_StartPosY:int = 0;

    public var m_AX:Number = 0.0;
    public var m_VY:Number = 0.0;

    public var m_GroundFlag:Boolean = false;

    //Collision
    public var m_ShapeDef_Black:b2PolygonDef;
    public var m_ShapeDef_White:b2PolygonDef;

    //#Debug
    public var m_TrgShape:Shape = new Shape();


    //==Function==

    //Init
    public function IGameObject(in_PhysWorld:b2World, in_X:int, in_Y:int, in_ColW:Number, in_ColH:Number, in_IsBlack:Boolean){
        //Pos
        {
            this.x = m_StartPosX = in_X;
            this.y = m_StartPosY = in_Y;
        }

        //Phys
        {
            //m_Body
            {
                var bodyDef:b2BodyDef;
                {
                    bodyDef = new b2BodyDef();
                    bodyDef.position.Set(in_X / PHYS_SCALE, in_Y / PHYS_SCALE);
                }

                m_Body = in_PhysWorld.CreateBody(bodyDef);
            }

            //Add Shape
            {
                //Shape
//                const COL_W:Number = GameMain.PANEL_LEN/2 / PHYS_SCALE;
//                const COL_W:Number = GameMain.PANEL_LEN*6/10 / PHYS_SCALE;
                const COL_W:Number = in_ColW/2 / PHYS_SCALE;
                const COL_H:Number = in_ColH/2 / PHYS_SCALE;
                {//m_ShapeDef_Black
                    m_ShapeDef_Black = new b2PolygonDef();
                    m_ShapeDef_Black.SetAsBox(COL_W, COL_H);
                    m_ShapeDef_Black.density = 1.0;
                    m_ShapeDef_Black.friction = 0.0;
                    //m_ShapeDef_Black.restitution = i_Param.restitution;

                    //同じ色同士でしかぶつからない
                    m_ShapeDef_Black.filter.categoryBits = (1 << 0);
                    m_ShapeDef_Black.filter.maskBits = (1 << 0);
                }
                {//m_ShapeDef_White
                    m_ShapeDef_White = new b2PolygonDef();
                    m_ShapeDef_White.SetAsBox(COL_W, COL_H);
                    m_ShapeDef_White.density = 1.0;
                    m_ShapeDef_White.friction = 0.0;
                    //m_ShapeDef_White.restitution = i_Param.restitution;

                    //同じ色同士でしかぶつからない
                    m_ShapeDef_White.filter.categoryBits = (1 << 1);
                    m_ShapeDef_White.filter.maskBits = (1 << 1);
                }

                if(in_IsBlack){
                    m_Body.CreateShape(m_ShapeDef_Black);
                }else{
                    m_Body.CreateShape(m_ShapeDef_White);
                }

                m_Body.SetMassFromShapes();
            }
        }

        //Debug
        {
            var g:Graphics = m_TrgShape.graphics;
            g.lineStyle(5, 0xFFFF00, 1.0);
            g.drawCircle(0, 0, 32);
            //addChild(m_TrgShape);//ここのコメントを外すと、移動方向の可視化
        }

        //Call Reset
        {
            addEventListener(
                Event.ADDED_TO_STAGE,//stageに触れるようになるまで遅延
                function(e:Event):void{
                    stage.addEventListener(
                        KeyboardEvent.KEY_DOWN,
                        function(e:KeyboardEvent):void{
                            switch(e.keyCode){
                            case 82://KEY_R
                                Reset();
                                break;
                            }
                        }
                    );
                }
            );
        }

        //Call Update
        {
            addEventListener(Event.ENTER_FRAME, Update);
        }
    }

    //Reset
    public function Reset():void{
        //位置を初期化するだけ
        this.x = m_StartPosX;
        this.y = m_StartPosY;

        m_Body.SetXForm(new b2Vec2(this.x / PHYS_SCALE, this.y / PHYS_SCALE), m_Body.GetAngle());
    }

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

        //範囲外なら移動を諦める
        {
            const Range:int = GameMain.PANEL_LEN*2;
            if(this.x < -Range){return;}
            if(this.x >= GameMain.W + Range){return;}
            if(this.y < -Range){return;}
            if(this.y >= GameMain.H + Range){return;}
        }

        //Phys => Game
        var PhysX:Number;
        var PhysY:Number;
        var PhysVX:Number;
        var PhysVY:Number;
        {
            PhysX = m_Body.GetPosition().x * PHYS_SCALE;
            PhysY = m_Body.GetPosition().y * PHYS_SCALE;

            PhysVX = m_Body.GetLinearVelocity().x * PHYS_SCALE;
            PhysVY = m_Body.GetLinearVelocity().y * PHYS_SCALE;
        }

        //加速&擬似摩擦補正
        {
            //VX
            {
                //
                PhysVX += m_AX * in_DeltaTime;

                //
                var Rat:Number;
                {
                    if(! m_GroundFlag){
                        Rat = DRAG_RATIO_O;
                    }else{
                        Rat = DRAG_RATIO_W;
                    }
                }

                //
                PhysVX *= Math.pow(Rat, 10.0*in_DeltaTime);
            }
        }

        //Bitmapを見た移動
        {
            var MoveX:int = PhysX - this.x;
            var MoveY:int = PhysY - this.y;

            //Debug
            {
                m_TrgShape.x = MoveX*5;
                m_TrgShape.y = MoveY*5;
            }

            //移動アルゴリズム実行

            const BaseOffset:int = 1;//浮く量
            var Offset:int;

            var TrgX:int;
            var TrgY:int;

            var i:int;

            //Move : Up
            {
                TrgX = this.x;
                Offset = (MoveY < 0)? BaseOffset-MoveY: BaseOffset;

                for(i = 0; i < Offset; i++){
                    TrgY = this.y - 1;
                    if(IsCollision(TrgX, TrgY)){
                        //天井にぶつかったので速度は0にする
                        PhysVY = 0;
                        break;
                    }
                    this.y = TrgY;
                }

                //途中で止まったなら、その分をPhys側にフィードバック
                PhysY += Offset - i;
            }

            //Move : Horz
            {
                TrgX = this.x;
                TrgY = this.y;

                for(i = 0; i != MoveX; ){
                    if(i < MoveX){
                        i++;
                        TrgX++;
                    }else{
                        i--;
                        TrgX--;
                    }

                    if(IsCollision(TrgX, TrgY)){
                        PhysVX = 0;
                        //途中で止まったなら、その分をPhys側にフィードバック
                        PhysX = this.x;//位置を一致させてしまう
                        break;
                    }

                    this.x = TrgX;
                }
            }

            //Move : Down
            {
                TrgX = this.x;
                Offset = (MoveY > 0)? BaseOffset+MoveY: BaseOffset;

                m_GroundFlag = false;
                for(i = 0; i < Offset; i++){
                    TrgY = this.y + 1;
                    if(IsCollision(TrgX, TrgY)){
                        //接地したとみなす
                        m_GroundFlag = true;//ジャンプできるようにフラグオン
                        PhysVY = 0;//速度リセット
                        break;
                    }
                    this.y = TrgY;
                }

                //途中で止まったなら、その分をPhys側にフィードバック
                PhysY -= Offset - i;
            }
        }

        //Game => Phys
        {
            //VY
            {
                if(m_VY < 0.0){
                    PhysVY = m_VY;

                    m_VY = 0.0;
                }
            }

            m_Body.SetXForm(new b2Vec2(PhysX / PHYS_SCALE, PhysY / PHYS_SCALE), m_Body.GetAngle());
            m_Body.GetLinearVelocity().x = PhysVX / PHYS_SCALE;
            m_Body.GetLinearVelocity().y = PhysVY / PHYS_SCALE;
        }
    }

    public function IsCollision(in_X:int, in_Y:int):Boolean{
        return GameMain.Instance().IsCollision(in_X, in_Y);
    }
}
//*/


//*
internal class Player extends IGameObject{

    //==Cost==

    //#Input
    static public var button_enum:int = 0;
    static public const BUTTON_L:int = button_enum++;
    static public const BUTTON_R:int = button_enum++;
    static public const BUTTON_U:int = button_enum++;
    static public const BUTTON_NUM:int = button_enum;

    //#Move
    static public const MOVE_POW_AIR:Number    = 470.0;
    static public const MOVE_POW_GROUND:Number = 1100.0;
    static public const JUMP_VEL:Number = 300.0;

    //==Var==

    //#Graphic
    public var m_Root:Sprite = new Sprite();

    //#Input
    public var m_Input:Array = new Array(BUTTON_NUM);

    //
    public var m_IsInBlack_Old:Boolean = false;

    //==Function==

    //#Init
    public function Player(in_PhysWorld:b2World, in_X:int, in_Y:int){
        //Super
        {
            var IsBlack:Boolean = true;
            const COL_W:Number = GameMain.PANEL_LEN-2;
            const COL_H:Number = GameMain.PANEL_LEN-2;//8;
            super(in_PhysWorld, in_X, in_Y, COL_W, COL_H, IsBlack);
        }

        //Pos
        {
            this.x = in_X;
            this.y = in_Y;
        }

        //Init Later (for Using "stage" etc.)
        addEventListener(Event.ADDED_TO_STAGE, Init);
    }

    public function Init(e:Event = null):void{
        var i:int;

        //Init Once Only
        {
            removeEventListener(Event.ADDED_TO_STAGE, Init);
        }

        //Layer
        {
            addChild(m_Root);
        }

        //Create Graphic
        {
            const Rad:int = GameMain.PANEL_LEN/4+1;

            var shape:Shape;
            var g:Graphics;

            //黒
            {
                shape = new Shape();
                g = shape.graphics;

                g.lineStyle(0, 0x000000, 0.0);
                g.beginFill(0x222222, 1.0);
                g.drawCircle(0, 0, Rad);
                g.endFill();

                m_Root.addChild(shape);
            }

            //白
            {
                shape = new Shape();
                g = shape.graphics;

                g.lineStyle(0, 0x000000, 0.0);
                g.beginFill(0xDDDDDD, 1.0);
                g.drawCircle(0, 0, Rad);
                g.endFill();

                m_Root.addChild(shape);

                shape.mask = GameMain.Instance().m_ShapeMask_ForPlayer;//地形が黒いところでしか白プレイヤーは表示されない
            }
        }

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

            //Listener
            stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP, KeyUp);
        }
    }

    //#Input
    public function KeyDown(e:KeyboardEvent):void{
        switch(e.keyCode){
        case Keyboard.LEFT:        m_Input[BUTTON_L] = true;        break;
        case Keyboard.RIGHT:    m_Input[BUTTON_R] = true;        break;
        case Keyboard.UP:        m_Input[BUTTON_U] = true;        break;
        }
    }

    public function KeyUp(e:KeyboardEvent):void{
        switch(e.keyCode){
        case Keyboard.LEFT:        m_Input[BUTTON_L] = false;        break;
        case Keyboard.RIGHT:    m_Input[BUTTON_R] = false;        break;
        case Keyboard.UP:        m_Input[BUTTON_U] = false;        break;
        }
    }

    //#Update
    override public function Update(e:Event=null):void{
        //入力
        {
            //m_AX
            {
                var PowX:Number;
                {
                    if(! m_GroundFlag){
                        PowX = MOVE_POW_AIR;
                    }else{
                        PowX = MOVE_POW_GROUND;
                    }
                }

                m_AX = 0.0;
                if(m_Input[BUTTON_R]){
                    m_AX =  PowX;
                }
                if(m_Input[BUTTON_L]){
                    m_AX = -PowX;
                }
            }

            //m_VY
            {
                if(m_GroundFlag && m_Input[BUTTON_U]){
                    m_VY = -JUMP_VEL;
                }
            }
        }

        //移動
        {
            super.Update();
        }

        //白黒が反転したら、コリジョンも反転する
        {
            var IsInBlack:Boolean = IsInBlack();
//*
            if(m_IsInBlack_Old != IsInBlack){//bool同士の比較は気が引けるが
                if(IsInBlack){
                    //Delete Old
                    m_Body.DestroyShape(m_Body.m_shapeList);
                    //Create New
                    m_Body.CreateShape(m_ShapeDef_White);
                }else{
                    //Delete Old
                    m_Body.DestroyShape(m_Body.m_shapeList);
                    //Create New
                    m_Body.CreateShape(m_ShapeDef_Black);
                }

                m_IsInBlack_Old = IsInBlack;
            }
//*/
/*
            //Debug
            if(IsInBlack){
                scaleX = 3;
            }else{
                scaleX = 1;
            }
//*/
        }
    }

    //Utility
    static public var BitmapData_ForHitTest:BitmapData;
    static public var mtx:Matrix = new Matrix();
    public function IsInBlack():Boolean{
        //今の自分の中心位置が、白く描画されていれば黒エリアの中にいると判断する

        if(BitmapData_ForHitTest == null){
//*
            BitmapData_ForHitTest = new BitmapData(1,1,true,0x00000000);
/*/
            //Debug
            BitmapData_ForHitTest = new BitmapData(32,32,true,0x88888888);
            GameMain.Instance().addChild(new Bitmap(BitmapData_ForHitTest));//test
//*/
        }

        //自分を描画して
        mtx.tx = -this.x;
        mtx.ty = -this.y;
        BitmapData_ForHitTest.draw(GameMain.Instance().m_Layer_Player, mtx);

        //白ならTrue
        return ((BitmapData_ForHitTest.getPixel32(0,0) & 0xFF) > 0xFF/2);
    }
}
//*/

//*
internal class MovableBlock extends IGameObject{
    //==Var==

    public var m_IsBlack:Boolean = true;

    //Mask For Player
    public var m_MaskForPlayer:Shape = new Shape();


    //==Function==

    public function MovableBlock(in_PhysWorld:b2World, in_X:int, in_Y:int){
        //Super
        {
            const COL_W:Number = GameMain.PANEL_LEN+1;
            super(in_PhysWorld, in_X, in_Y, COL_W, COL_W, m_IsBlack);
        }

        const BlockW:int = GameMain.PANEL_LEN+2;//*3/2;//+2;
        const BlockH:int = GameMain.PANEL_LEN+2;//*3/2;//*3/2;//+2;
        const OffsetY:int = 0;//-1;//GameMain.PANEL_LEN/8;

        //Graphic
        {
            var shape:Shape = new Shape();
            var g:Graphics = shape.graphics;
            {
                g.lineStyle(2, 0x888888, 1.0);
//                g.lineStyle(0, 0x000000, 0.0);
                g.beginFill(0x000000, 1.0);
                g.drawRect(-BlockW/2, -BlockH/2+OffsetY, BlockW, BlockH);
                g.endFill();
            }

            addChild(shape);
        }

        //Mask For Player
        {
            g = m_MaskForPlayer.graphics;
            if(m_IsBlack)
            {
                g.lineStyle(2, 0x888888, 1.0);
                g.beginFill(0x000000, 1.0);
                g.drawRect(-BlockW/2, -BlockH/2+OffsetY, BlockW, BlockH);
                g.endFill();
            }

            GameMain.Instance().m_ShapeMask_ForPlayer.addChild(m_MaskForPlayer);
        }
    }

    //#Update
    override public function Update(e:Event=null):void{
        var OldX:int = this.x;
        var OldY:int = this.y;

        //移動
        {
            super.Update();
        }

        //Dirty
        {
            var NewX:int = this.x;
            var NewY:int = this.y;

            if(OldX != NewX || OldY != NewY){//移動していたら
                //自分の周辺のコリジョンを再描画
                var lx:int, rx:int, uy:int, dy:int;
                {
                    const Range:int = GameMain.PANEL_LEN;
                    if(OldX < NewX){
                        lx = OldX - Range;
                        rx = NewX + Range;
                    }else{
                        lx = NewX - Range;
                        rx = OldX + Range;
                    }

                    if(OldY < NewY){
                        uy = OldY - Range;
                        dy = NewY + Range;
                    }else{
                        uy = NewY - Range;
                        dy = OldY + Range;
                    }
                }

                GameMain.Instance().AddDirty(lx, rx, uy, dy, m_IsBlack);
            }
        }

        //Mask For Player
        {
            m_MaskForPlayer.x = this.x;
            m_MaskForPlayer.y = this.y;
        }
    }

    override public function IsCollision(in_X:int, in_Y:int):Boolean{
        if(m_IsBlack){
            return GameMain.Instance().IsCollision_ForBlackBlock(in_X, in_Y);
        }else{
            return GameMain.Instance().IsCollision_ForWhiteBlock(in_X, in_Y);
        }
    }
}
//*/

//*
internal class Goal extends Sprite{
    public var m_GoalFlag:Boolean = false;

    public function Goal(in_X:int, in_Y:int){
        //Pos
        {
            this.x = in_X;
            this.y = in_Y;
        }

        //Graphic
        {
            var shape:Shape = new Shape();
            var g:Graphics = shape.graphics;
            g.lineStyle(2, 0xFFFF00, 1.0);
            g.drawCircle(0, 0, GameMain.PANEL_LEN/2);

            shape.filters = [new GlowFilter(0xFF0000, 1.0)];

            addChild(shape);
        }

        //Call Reset
        {
            addEventListener(
                Event.ADDED_TO_STAGE,//stageに触れるようになるまで遅延
                function(e:Event):void{
                    stage.addEventListener(
                        KeyboardEvent.KEY_DOWN,
                        function(e:KeyboardEvent):void{
                            switch(e.keyCode){
                            case 82://KEY_R
                                Reset();
                                break;
                            }
                        }
                    );
                }
            );
        }

        //Call "Update"
        {
            addEventListener(Event.ENTER_FRAME, Update);
        }
    }


    //#Update
    public function Update(e:Event=null):void{
        var GapX:int = GameMain.Instance().m_Player.x - this.x;
        var GapY:int = GameMain.Instance().m_Player.y - this.y;
        if(Math.sqrt(GapX*GapX+GapY*GapY) < GameMain.PANEL_LEN){
            //プレイヤーがゴールに接触した
            //じゃあ、なんかゴールを表示しとこう
            var shape:Shape = new Shape();
            var g:Graphics = shape.graphics;
            g.lineStyle(8, 0xFFFF00, 1.0);
            g.drawCircle(0,0, 100);
            shape.filters = [new GlowFilter(0xFF0000, 1.0)];
            addChild(shape);

            //こいつの処理も外す
            removeEventListener(Event.ENTER_FRAME, Update);
            //本体も外しとこう
            GameMain.Instance().removeEventListener(Event.ENTER_FRAME, GameMain.Instance().Update);
            //プレイヤーも(ry
            GameMain.Instance().m_Player.removeEventListener(Event.ENTER_FRAME, GameMain.Instance().m_Player.Update);

            //リセットのためにフラグを記憶
            m_GoalFlag = true;
        }
    }

    public function Reset():void{
        //リセットまわりはかなりてきとー
        if(m_GoalFlag){
            removeChildAt(1);

            addEventListener(Event.ENTER_FRAME, Update);
            GameMain.Instance().addEventListener(Event.ENTER_FRAME, GameMain.Instance().Update);
            GameMain.Instance().m_Player.addEventListener(Event.ENTER_FRAME, GameMain.Instance().m_Player.Update);

            m_GoalFlag = false;
        }
    }
}
//*/