2019-07-06

[Flash] Box2DFlashAS3 (2)

[Flash] Box2DFlashAS3 を学習 (2)

Environment:

  • Adobe AIR SDK 22
  • Box2DFlashAS3 v2.0.2
  • FlashDevelop 5.2
  • Windows 7 64bit

[話者] Physics Engine(物理演算エンジン)の学びを継続。

Box2DでActionScript物理プログラミング 技術評論社 の記事をひねってみる。

Learn

  • 第2回 物理エンジンをセットアップし,箱を落とすFlashを作る
  • 第3回 マウスのドラッグ&ドロップで,好きなサイズの箱を作る
  • 第4回 いろいろな形の物体を作る

をまとめてみた。

実Flash。操作可。マウスドラッグで物体の大きさを決める。シフトキーでスロー(5分の1)になる。

SourceCode

Main.as

package{
    import Box2D.Collision.Shapes.b2CircleDef;
    import Box2D.Collision.b2AABB;
    import Box2D.Collision.Shapes.b2PolygonDef;
    import Box2D.Common.Math.b2Vec2;
    import Box2D.Dynamics.b2Body;
    import Box2D.Dynamics.b2BodyDef;
    import Box2D.Dynamics.b2DebugDraw;
    import Box2D.Dynamics.b2World;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    
    /**
     * ...
     * @author foo
     */
    [SWF(width = "480", height = "480", frameRate = "30", backgroundColor = "#02143")]
    // 480px -> 4.8m
    public class Main extends Sprite {
        private var wld:b2World = null; //world
        private var wldfps:int = 30;   //30 fps
        private var items:Array = [];
        private const PIXELS_PER_METER:Number = 100;  // 100pixel for 1m
        private var objPt:Point = null; // object point
        private var tf:TextField = null;
        private var shp:Array = ['box', 'circle', 'triangle'];
    
        public function Main() {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUP);
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
        }
        
        private function onEnterFrame(e:Event):void {
            if (wld == null) {
                setupPhisycsWorld();
            }
            wld.Step(1 / wldfps, 10); //30fps, PhisycsEnginePrecision: more than 10 -> more precise, slower
            
            // remove objects out of world
            var obj:b2Body = wld.GetBodyList();
            while (obj) {
                if (obj.GetWorldCenter().y * PIXELS_PER_METER > 1000) {
                    wld.DestroyBody(obj);
                }
                obj = obj.GetNext();
            }
            
            // drag temp box
            if ( ! objPt) return;
            graphics.lineStyle(1, 0xCCCCCC, 0.3);
            graphics.beginFill(0x888888, 0.3);
            graphics.drawRect(
                objPt.x,
                objPt.y,
                Math.abs(stage.mouseX - objPt.x),
                Math.abs(stage.mouseY - objPt.y)
            );
            graphics.endFill();
            if (tf) tf.text = 
                "mouseX = " + stage.mouseX
                + "\nmouseY = " + stage.mouseY
                + "\nstartX = " + objPt.x
                + "\nstartY = " + objPt.y
        }
        
        private function onKeyUP(e:KeyboardEvent):void {
            wldfps = 30;
        }
        
        private function onKeyDown(e:KeyboardEvent):void {
            if (e.shiftKey) wldfps = 150;  // 5 times slower
        }
        
        private function onMouseDown(e:MouseEvent):void {
            if ( ! objPt) objPt = new Point();
            objPt.x = e.stageX;
            objPt.y = e.stageY;
        }
        
        private function onMouseUp(e:MouseEvent):void {
            var center_x:Number = (e.stageX + objPt.x) / 2.0 / PIXELS_PER_METER;
                    //mouseX(250px)÷DebugDraw.m_drawScale(100) -> 2.5m from left
            var center_y:Number = (e.stageY + objPt.y) / 2.0 / PIXELS_PER_METER;
                    //monseY(100px)÷DebugDraw.m_drawScale(100) -> 1m from top
            var w:Number = Math.abs(e.stageX - objPt.x) / PIXELS_PER_METER;
            var h:Number = Math.abs(e.stageY - objPt.y) / PIXELS_PER_METER;
            if (w < 0.05) w = 0.1;
            if (h < 0.05) h = 0.1
            objPt = null;

            var objPos_m:b2BodyDef = new b2BodyDef();       // BoxPosition_meter
            objPos_m.position.Set(center_x, center_y); 
            items.push(wld.CreateBody(objPos_m));
            var cur:int = items.length - 1;                 // current item
            var polShp_m:b2PolygonDef = null;
            var ccShp_m:b2CircleDef = null;
            var i:int = 0;
            
            if (shp[cur % shp.length] == 'box') {
                // setup box
                polShp_m = new b2PolygonDef();      //BoxShape_meter
                var slant_r:Number = (Math.random() * 3.14 - (3.14 / 2)) / 360 * 30; //slant radian
                    // radian 0to+3.14 -> radian -1.62to+1.62 -> degree -15to+15
                polShp_m.SetAsOrientedBox(w/2.0, h/2.0, new b2Vec2(0, 0), slant_r);
                                                    // Oriented = slanted(傾き)
                                                    // width 0.6m, height 0.4m, rotate Center, 0.8radian=45degree
                polShp_m.density = 1;               // weight 1kg/m^2 for 1m^2 box; now 0.24kg(240g) for 0.24m^2 box
                polShp_m.restitution = 0.2;         // rebound: 0:no_rebound to 1:rebound_forever
                items[cur].CreateShape(polShp_m);
            } else if (shp[cur % shp.length] == 'circle') {
                ccShp_m = new b2CircleDef();
                ccShp_m.radius = Math.sqrt(Math.pow(w / 2.0, 2) + Math.pow(h / 2.0, 2));
                ccShp_m.density = 1.0;
                ccShp_m.restitution = 0.5;
                items[cur].CreateShape(ccShp_m);
            } else if (shp[cur % shp.length] == 'triangle') {
                polShp_m = new b2PolygonDef();
                var pt:Array = [];
                pt.push(new Point(center_x - (w / 2.0), center_y - (h / 2.0)));
                pt.push(new Point(center_x + (w / 2.0), center_y - (h / 2.0)));
                pt.push(new Point(center_x + (w / 2.0), center_y + (h / 2.0)));
                polShp_m.vertexCount = pt.length;
                for (i = 0; i < pt.length; i++) {
                    polShp_m.vertices[i].Set(pt[i].x - center_x, pt[i].y - center_y);
                }
                polShp_m.density = 1.0;
                polShp_m.restitution = 0.5;
                polShp_m.friction = 0.5;
                items[cur].CreateShape(polShp_m);
            }
            items[cur].SetMassFromShapes();        // set weight
        }
        
        private function setupPhisycsWorld():void {
            //log
            var tfmt:TextFormat = new TextFormat('Courier New', 16, 0xFFFFFF, true)
            tf = new TextField();
            tf.x = 10;
            tf.y = 10;
            tf.autoSize = TextFieldAutoSize.LEFT;
            tf.defaultTextFormat = tfmt;
            tf.text = 'Drag with your mouse button!';
            addChild(tf);

            // setup Phisycs Engine
            var wldPos_m:b2AABB = new b2AABB();    // worldPosition_meter: Axis-aligned(not slanted) BoundingBox
            wldPos_m.lowerBound.Set( -100, -100);  // left top -100m,-100m (length 200m)
            wldPos_m.upperBound.Set(100, 100);     // right bottom
            var gv_mps:b2Vec2 = new b2Vec2(0, 10); // gravity_meterPerSec: 0m/s^2 to right, 10m/s^2 to bottom
            wld = new b2World(wldPos_m, gv_mps, true); // doSleep: improve performance by not simulating inactives
            
            // setup floor
            //  floor position
            var flrPos_m:b2BodyDef = new b2BodyDef();  // floorPosition_meter
            flrPos_m.position.Set(2.5, 4);             // 2.5m from left, 4m from top
            //  floor shape
            var flrSh_m:b2PolygonDef = new b2PolygonDef();
            flrSh_m.SetAsBox(2, 0.1);                  // width 4m, height 0.2m(20cm)
            //  put floor (unmovable)
            var flr:b2Body = wld.CreateBody(flrPos_m);
            flr.CreateShape(flrSh_m);

            // box2d draw
            var dd_m:b2DebugDraw = new b2DebugDraw();   // debugDraw_meter
            dd_m.m_sprite = this;
            dd_m.m_drawScale = PIXELS_PER_METER;        // 100pixel for 1m
            dd_m.m_fillAlpha = 0.3;             // alpha(see-ability): 0:no_see(transparent) to 1:see(no_transparent)
            dd_m.m_lineThickness = 1;           // line width 1pixel
            dd_m.m_drawFlags = b2DebugDraw.e_shapeBit;
            wld.SetDebugDraw(dd_m);
        }
    }
}

0 件のコメント:

コメントを投稿

人気記事