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

Box2D試作:光源(視界)処理

Box2Dと視界(光源)アルゴリズムの組み合わせ方のテスト。
Get Adobe Flash player
by o_healer 09 Feb 2011
/**
 * 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/7dgb
 */

package {
    import flash.display.*;
    import flash.events.*;
    import flash.filters.*;
    import flash.geom.*;
    import flash.net.*;
    import flash.system.*;
    import flash.text.*;

    //Box2D
    import Box2D.Dynamics.*;
    import Box2D.Collision.*;
    import Box2D.Collision.Shapes.*;
    import Box2D.Common.*;
    import Box2D.Common.Math.*;
    import Box2D.Dynamics.Joints.*;

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

        //画面サイズ
        static public const VIEW_W:int = 465;
        static public const VIEW_H:int = 465;

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

        //Box2Dと実際の表示の比率(Box2Dに大きすぎる値を入れると上手く動かなかったりする)
        static public const PHYS_SCALE:Number = 10;
        static public function PHYS_to_IMAGE(in_Val:Number):Number{return in_Val * PHYS_SCALE;}
        static public function IMAGE_to_PHYS(in_Val:Number):Number{return in_Val / PHYS_SCALE;}
        //Box2Dの画面外の余白
        static public const BOX2D_RANGE_OFFSET:int = 100;


        //==Var==

        //Layer
        public var m_Layer_ShadowCast:Sprite = new Sprite();


        //Box2D
        public var m_Box2D_World:b2World;

        //光源の球
        public var m_LightBody:b2Body;

        //マウスで動かせる球
        public var m_MouseBallBody:b2Body;
        public var m_MouseJoint:b2MouseJoint;

        //境界線を描くためのGraphics
        public var m_LightShape:Shape = new Shape();
        public var m_LightGraphics:Graphics = m_LightShape.graphics;

        //光源の表示用Bitmap
        public var m_BitmapData_Light:BitmapData = new BitmapData(VIEW_W, VIEW_H, true, 0x00000000);

        //==Function==

        //Init
        public function GameMain(){
            //Layer
            {
                addChild(m_Layer_ShadowCast);
            }

            //Box2D
            {
                //考慮する領域
                var worldAABB:b2AABB = new b2AABB();
                worldAABB.lowerBound.Set(IMAGE_to_PHYS(-BOX2D_RANGE_OFFSET), IMAGE_to_PHYS(-BOX2D_RANGE_OFFSET));
                worldAABB.upperBound.Set(IMAGE_to_PHYS(VIEW_W+BOX2D_RANGE_OFFSET), IMAGE_to_PHYS(VIEW_H+BOX2D_RANGE_OFFSET));
                //重力ベクトル
                var gravity:b2Vec2 = new b2Vec2(0.0, GRAVITY);
                //Sleep
                var useSleep:Boolean = true;
                //物理world
                m_Box2D_World = new b2World(worldAABB, gravity, useSleep);
            }

            //周囲
            {
                //D
                CreateCollision_Box(
                    VIEW_W/2,//X
                    VIEW_H - 5,//Y

                    VIEW_W,//W
                    10,//H

                    0,//Rot

                    true//IsFix
                );

                //U
                CreateCollision_Box(
                    VIEW_W/2,//X
                    5,//Y

                    VIEW_W,//W
                    10,//H

                    0,//Rot

                    true//IsFix
                );

                //L
                CreateCollision_Box(
                    5,//X
                    VIEW_H/2,//Y

                    10,//W
                    VIEW_H,//H

                    0,//Rot

                    true//IsFix
                );

                //R
                CreateCollision_Box(
                    VIEW_W - 5,//X
                    VIEW_H/2,//Y

                    10,//W
                    VIEW_H,//H

                    0,//Rot

                    true//IsFix
                );
            }

            //段差
            {
                CreateCollision_Box(
                    VIEW_W*1/4,//X
                    VIEW_H*3/4,//Y

                    VIEW_W/4,//W
                    10//H
                );

                CreateCollision_Box(
                    VIEW_W*3/4,//X
                    VIEW_H*3/4,//Y

                    VIEW_W/4,//W
                    10//H
                );

                CreateCollision_Box(
                    VIEW_W*3/4,//X
                    VIEW_H*1/4,//Y

                    VIEW_W/4,//W
                    10//H
                );
            }

            //Light
            {
                CreateCollision_LightSphere(
                    VIEW_W/2, VIEW_H/2, 6
                );
            }

            //Mouse
            {
                CreateCollision_MouseSphere(
                    10
                );
            }

            //m_BitmapData_Light
            {
                addChild(new Bitmap(m_BitmapData_Light));
                //addChild(m_LightShape);
            }

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


        //Create : Collision : Box
        public function CreateCollision_Box(in_X:int, in_Y:int, in_W:int, in_H:int, in_Rot:Number = 0, in_FixFlag:Boolean = false):void{
            //Image
            var sprite:Sprite;
            {
                sprite = new Sprite();
                sprite.graphics.beginFill(0xDDDDDD);
                sprite.graphics.drawRect(-in_W/2, -in_H/2, in_W, in_H);
                sprite.graphics.endFill();
                m_Layer_ShadowCast.addChild(sprite);
            }

            //Shape Def
            var shapeDef:b2PolygonDef;
            {
                shapeDef = new b2PolygonDef();
                shapeDef.SetAsBox(IMAGE_to_PHYS(in_W/2), IMAGE_to_PHYS(in_H/2));
                if(in_FixFlag){
                    shapeDef.density = 0;//Fix
                }else{
                    shapeDef.density = 1;//tekitou
                }
            }

            //Body Def
            var bodyDef:b2BodyDef;
            {
                bodyDef = new b2BodyDef();
                bodyDef.position.Set(IMAGE_to_PHYS(in_X), IMAGE_to_PHYS(in_Y));
                bodyDef.angle = in_Rot;
            }

            //Body
            var body:b2Body;
            {
                body = m_Box2D_World.CreateBody(bodyDef);
                body.CreateShape(shapeDef);
                body.SetMassFromShapes();
                body.m_userData = sprite;
            }
        }

        //Create : Collision : Sphere for Light
        public function CreateCollision_LightSphere(in_X:int, in_Y:int, in_R:int, in_FixFlag:Boolean = false):void{
            //Image
            var sprite:Sprite;
            {
                sprite = new Sprite();
                sprite.graphics.beginFill(0xFFFF88);
                sprite.graphics.drawCircle(0,0, in_R);
                sprite.graphics.endFill();
                sprite.filters = [new GlowFilter(0xBBBB00)];
                addChild(sprite);
            }

            //Shape Def
            var shapeDef:b2CircleDef;
            {
                shapeDef = new b2CircleDef();
                shapeDef.radius = IMAGE_to_PHYS(in_R);
                if(in_FixFlag){
                    shapeDef.density = 0;//Fix
                }else{
                    shapeDef.density = 1;//tekitou
                }
            }

            //Body Def
            var bodyDef:b2BodyDef;
            {
                bodyDef = new b2BodyDef();
                bodyDef.position.Set(IMAGE_to_PHYS(in_X), IMAGE_to_PHYS(in_Y));
            }

            //Body
            var body:b2Body;
            {
                body = m_Box2D_World.CreateBody(bodyDef);
                body.CreateShape(shapeDef);
                body.SetMassFromShapes();
                body.m_userData = sprite;
            }

            m_LightBody = body;
        }

        //Create : Collision : Sphere for Mouse
        public function CreateCollision_MouseSphere(in_R:int):void{
            //Image
            var sprite:Sprite;
            {
                sprite = new Sprite();
                sprite.graphics.beginFill(0xDDDDDD);
                sprite.graphics.drawCircle(0,0, in_R);
                sprite.graphics.endFill();
                m_Layer_ShadowCast.addChild(sprite);
            }

            //Shape Def
            var shapeDef:b2CircleDef;
            {
                shapeDef = new b2CircleDef();
                shapeDef.radius = IMAGE_to_PHYS(in_R);
                shapeDef.density = 1;//tekitou
            }

            //Body Def
            var bodyDef:b2BodyDef;
            {
                bodyDef = new b2BodyDef();
                bodyDef.position.Set(IMAGE_to_PHYS(30), IMAGE_to_PHYS(30));
            }

            //Body
            var body:b2Body;
            {
                body = m_Box2D_World.CreateBody(bodyDef);
                body.CreateShape(shapeDef);
                body.SetMassFromShapes();
                body.m_userData = sprite;
            }

            //Joint
            {
                var mjd:b2MouseJointDef = new b2MouseJointDef();
                mjd.body1 = m_Box2D_World.GetGroundBody();
                mjd.body2 = body;
                //mjd.target.Set(IMAGE_to_PHYS(mouseX), IMAGE_to_PHYS(mouseY));
                mjd.target.Set(bodyDef.position.x, bodyDef.position.y);
                mjd.maxForce = 300.0 * body.GetMass();
                mjd.timeStep = 1.0/30.0;
                m_MouseJoint = m_Box2D_World.CreateJoint(mjd) as b2MouseJoint;
            }
        }

        //Update
        public function Update(e:Event=null):void{
            //今回進める時間
            var DeltaTime:Number = 1.0/30.0;

            //物理まわりの更新
            Update_Phys(DeltaTime);

            //光源(視界)処理
            Update_Light();
        }

        //物理まわりの更新
        public function Update_Phys(in_DeltaTime:Number):void{
            //マウスのところに移動
            m_MouseJoint.SetTarget(new b2Vec2(IMAGE_to_PHYS(mouseX), IMAGE_to_PHYS(mouseY)));

            //物理エンジンをDeltaTimeだけ進める
            m_Box2D_World.Step(in_DeltaTime, 10);

            //
            for (var b:b2Body = m_Box2D_World.m_bodyList; b; b = b.m_next) {
                //画像との同期
                if(b.m_userData != null){
                    var sprite:Sprite = b.m_userData as Sprite;
                    sprite.x = PHYS_to_IMAGE(b.GetPosition().x);
                    sprite.y = PHYS_to_IMAGE(b.GetPosition().y);
                    //b.m_userData.m_VX = PHYS_to_IMAGE(b.m_linearVelocity.x);
                    //b.m_userData.m_VY = PHYS_to_IMAGE(b.m_linearVelocity.y);

                    sprite.rotation = b.GetAngle() * 180/Math.PI;
                }

                //擬似摩擦
                {
                    const Ratio:Number = 0.9;//本当はDeltaTime依存の値にした方が良い
                    b.GetLinearVelocity().x *= Ratio;
                    b.GetLinearVelocity().y *= Ratio;
                    b.SetAngularVelocity(b.GetAngularVelocity() * Ratio);
                }
            }
        }

        //光源(視界)処理
        public function Update_Light():void{
            //初期化
            m_LightGraphics.clear();
            m_LightGraphics.lineStyle(1,0x00FFFF,1.0);
            var SrcX:Number = PHYS_to_IMAGE(m_LightBody.GetPosition().x);
            var SrcY:Number = PHYS_to_IMAGE(m_LightBody.GetPosition().y);
            var DstX:Number;
            var DstY:Number;
            var Distance:Number;

            //境界線を描く
            for (var b:b2Body = m_Box2D_World.m_bodyList; b; b = b.m_next) {
                //Check
                {
                    if(b.m_userData == null){continue;}
                    if(m_LightBody == b){continue;}
                }

                var xf:b2XForm     = b.m_xf;
                for (var s:b2Shape = b.GetShapeList(); s; s = s.m_next){
                    switch (s.m_type){
                    case b2Shape.e_circleShape:
                        {
                            var circle:b2CircleShape = (s as b2CircleShape);
                            
                            var Center:b2Vec2 = b2Math.b2MulX(xf, circle.m_localPosition);
                            var CenterX:Number = PHYS_to_IMAGE(Center.x);
                            var CenterY:Number = PHYS_to_IMAGE(Center.y);
                            var Rad:Number = PHYS_to_IMAGE(circle.m_radius);

                            var GapX:Number = SrcX - CenterX;
                            var GapY:Number = SrcY - CenterY;
                            Distance = Math.sqrt(GapX*GapX + GapY*GapY);

                            if(Rad < Distance){
                                //擬似計算。厳密にはDistanceとRadから角度なりを求めて厳密な接点を求めるべき。
                                var SideX:Number =  GapY*Rad/Distance;
                                var SideY:Number = -GapX*Rad/Distance;

                                DstX = CenterX + SideX;
                                DstY = CenterY + SideY;
//                                m_LightGraphics.moveTo(SrcX, SrcY);
//                                m_LightGraphics.lineTo(DstX, DstY);
                                m_LightGraphics.moveTo(DstX, DstY);
                                m_LightGraphics.lineTo(DstX + VIEW_W/Distance * (DstX-SrcX), DstY + VIEW_H/Distance * (DstY-SrcY));

                                SideX = -SideX;
                                SideY = -SideY;
                                DstX = CenterX + SideX;
                                DstY = CenterY + SideY;
//                                m_LightGraphics.moveTo(SrcX, SrcY);
//                                m_LightGraphics.lineTo(DstX, DstY);
                                m_LightGraphics.moveTo(DstX, DstY);
                                m_LightGraphics.lineTo(DstX + VIEW_W/Distance * (DstX-SrcX), DstY + VIEW_H/Distance * (DstY-SrcY));
                            }
                        }
                        break;
                    
                    case b2Shape.e_polygonShape:
                        {
                            var i:int;
                            var poly:b2PolygonShape = (s as b2PolygonShape);
                            var vertexCount:int = poly.GetVertexCount();
                            var localVertices:Array = poly.GetVertices();
                            
                            //b2Assert(vertexCount <= b2_maxPolygonVertices);
                            var vertices:Array = new Array(b2Settings.b2_maxPolygonVertices);
                            
                            for (i = 0; i < vertexCount; ++i)
                            {
                                vertices[i] = b2Math.b2MulX(xf, localVertices[i]);

                                DstX = PHYS_to_IMAGE(vertices[i].x);
                                DstY = PHYS_to_IMAGE(vertices[i].y);

                                //不要かもしれないが、一応画面からギリギリはみ出るくらいの線にしておく
                                Distance = Math.min(Math.abs(DstX-SrcX), Math.abs(DstY-SrcY));
                                if(Distance < 1){Distance = 1;}
//                                m_LightGraphics.moveTo(SrcX, SrcY);
//                                m_LightGraphics.lineTo(DstX, DstY);
                                m_LightGraphics.moveTo(DstX, DstY);
                                m_LightGraphics.lineTo(DstX + VIEW_W/Distance * (DstX-SrcX), DstY + VIEW_H/Distance * (DstY-SrcY));
                            }
                        }
                        break;
                    }
                }
            }


            //=実際の光の範囲の描画=

            const CT_FORCE_DARK:ColorTransform = new ColorTransform(0,0,0,1, 0,0,1,-0);

            //まずはリセット
            {
                m_BitmapData_Light.fillRect(m_BitmapData_Light.rect, 0x80000000);
            }

            //境界線をDraw
            {
                m_BitmapData_Light.draw(m_LightShape, null, CT_FORCE_DARK);
            }

            //物体もDraw
            {
                m_BitmapData_Light.draw(m_Layer_ShadowCast, null, CT_FORCE_DARK);
            }

            //光源の位置からFloodFill
            {
                m_BitmapData_Light.floodFill(SrcX, SrcY, 0x80FFFFFF);
            }
        }
    }
}