Beautifl - Flash Gallery

Thumbnail : Cart
Cart
flashrod 2010-03-19 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

// スリックカート (Box2D版)
// [↑] アクセル
// [←] [→] ステアリング
// [↓] ブレーキ
// [SPC] リセット
package {
    import Box2D.Collision.Shapes.b2CircleDef;
    import Box2D.Collision.Shapes.b2PolygonDef;
    import Box2D.Collision.b2AABB;
    import Box2D.Common.Math.b2Vec2;
    import Box2D.Dynamics.b2Body;
    import Box2D.Dynamics.b2BodyDef;
    import Box2D.Dynamics.b2World;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.utils.getTimer;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.display.Loader;

    public class Cart2D extends Sprite {
        // ビューポート
        private static const VW:Number = 465;
        private static const VH:Number = 465;
        // ワールド
        private static const W:Number = 465;
        private static const H:Number = 3000;
        private static const CX:Number = W/2;
        private static const CY:Number = H/2;
        // スタート・ゴールライン(ワールド座標系)
        private static const START_Y:Number = H-50;
        private static const GOAL_Y:Number = 50;
        // パラメータ類
        private static const A_FORCE:Number = 20000;
        private static const S_FORCE:Number = A_FORCE * 0.7;
        private static const B_DAMP:Number = 1.0;
        private static const NB_DAMP:Number = 0.1;
        // インタラクション
        private static const VK_LEFT:int = 37;
        private static const VK_UP:int = 38;
        private static const VK_RIGHT:int = 39;
        private static const VK_DOWN:int = 40;
        private static const VK_SPC:int = 32;

        /** アクセルを踏んでいるときは真 */
        private var acl:Boolean = false;
        /** ブレーキを踏んでいるときは真 */
        private var brk:Boolean = false;
        /** ステアリング(左:-90°,右:90°)(radian)*/
        private var steering:Number = 0;

        /** ワールド */
        private var world:b2World;
        /** 車モデル */
        private var car:b2Body;
        /** 壁モデル */
        private var fixed:Array; // of b2Body
        /** 障害物モデル */
        private var float:Array; // of b2Body

        /** ワールドビュー */
        private var worldView:Sprite;

        /** スタートからの経過時間(ミリ秒) */
        private var time:int;

        /** カート2D構築 */
        public function Cart2D() {
            var keyTableDOWN:Array = [];
            keyTableDOWN[VK_UP] = function():void { acl = true; };
            keyTableDOWN[VK_DOWN] = function():void { brk = true; };
            keyTableDOWN[VK_LEFT] = function():void { steering =-Math.PI/2; };
            keyTableDOWN[VK_RIGHT] = function():void { steering = Math.PI/2; };
            keyTableDOWN[VK_SPC] = reset;
            stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent):void {
                var f:Function = keyTableDOWN[e.keyCode];
                if (f != null) {
                    f();
                }
            });
            var keyTableUP:Array = [];
            keyTableUP[VK_UP] = function():void { acl = false; };
            keyTableUP[VK_DOWN] = function():void { brk = false; };
            keyTableUP[VK_LEFT] = function():void { steering = 0; };
            keyTableUP[VK_RIGHT] = function():void { steering = 0; };
            stage.addEventListener(KeyboardEvent.KEY_UP, function(e:KeyboardEvent):void {
                var f:Function = keyTableUP[e.keyCode];
                if (f != null) {
                    f();
                }
            });

            // モデルを作る
            this.world = createWorld(); // ワールドを作る
            this.car = createCarBody(); // 車モデルを作る
            this.fixed = createFixed(); // 壁を作る
            this.float = createFloat(); // 障害物を作る

            // ビューを作る
            this.worldView = new Sprite();
            addChild(worldView);
            createBackground();
            createFixedView();
            createFloatView();

            // 時間表示
            var tf:TextField = new TextField();
            var fmt:TextFormat = new TextFormat();
            fmt.color = 0xFFFFFF;
            fmt.font  = 'Verdana';
            tf.defaultTextFormat = fmt;
            addChild(tf);

            // 背景を塗る
            graphics.beginFill(0x888888);
            graphics.drawRect(0, 0, VW, VH);
            graphics.endFill();

            // 車ビューを作る
            var carView:DisplayObject = createCarView();
            worldView.addChild(carView);
            car.SetUserData(carView); // 車モデルに車ビューを関連付け

            addEventListener(Event.ENTER_FRAME, function(e:Event):void {
                step();
                if (countup) {
                    var t:int = getTimer() - time;
                    var s:int = int(t / 1000);
                    var ms:int = (t - s * 1000) / 10;
                    tf.text = s+'"'+ms;
                }
            });
        }

        /** リセット
         */
        private function reset():void {
            car.SetXForm(new b2Vec2(CX, START_Y+16), 0);
            car.SetLinearVelocity(new b2Vec2());
            car.SetAngularVelocity(0);
        }

        /** ワールドを作る
         * @return ワールド
         */
        private function createWorld():b2World {
            var aabb:b2AABB = new b2AABB();
            aabb.lowerBound.Set(0, 0);
            aabb.upperBound.Set(W, H);
            var gravity:b2Vec2 = new b2Vec2();
            return new b2World(aabb, gravity, true);
        }

        /** 車モデルを作る
         * @return 車モデル
         */
        private function createCarBody():b2Body {
            var def:b2BodyDef = new b2BodyDef();
            def.position.Set(CX, START_Y+16);
            def.angularDamping = 0.75; // 回転の減衰
            var body:b2Body = world.CreateBody(def);
            var rect:b2PolygonDef = new b2PolygonDef();
            rect.SetAsBox(16, 16);
            rect.density = 1;       // 0:固定 kg/m^2
            rect.restitution = 0.4; // 反発係数[0,1]
            rect.friction = 0.1;    // 摩擦[0,1]
            body.CreateShape(rect);
            body.SetMassFromShapes();
            return body;
        }

        /** 車ビューを作る
         * @return 車表示オブジェクト
         */
        private function createCarView():DisplayObject {
            var sprite:Sprite = new Sprite();
            var image:Loader = new Loader();
            image.load(new URLRequest("http://img.f.hatena.ne.jp/images/fotolife/f/flashrod/20100121/20100121222408.png?1264080327"));
            image.x = -32/2;
            image.y = -32/2;
            sprite.addChild(image);
            return sprite;
        }

        /** 動かない壁をつくる
         */
        private function createFixed():Array {
            var a:Array = [
                {x:388, y:120, w:100, h:8, a:0.7853981633974483},
                {x:73, y:120, w:100, h:8, a:-0.7853981633974483},
                {x:124, y:764, w:116.25, h:8, a:0},
                {x:343, y:1226, w:116.25, h:8, a:0},
                {x:119, y:1612, w:116.25, h:8, a:0},
                {x:348, y:2000, w:140, h:8, a:0.7853981633974483},
                {x:116, y:2500, w:140, h:8, a:-0.7853981633974483},
                {x:0, y:1500, w:8, h:1500, a:0},
                {x:465, y:1500, w:8, h:1500, a:0},
                {x:232, y:3000, w:232, h:8, a:0},
            ];
            var list:Array = [];
            for each (var o:* in a) {
                var def:b2BodyDef = new b2BodyDef();
                def.position.Set(o.x, o.y);
                def.angle = o.a;
                var body:b2Body = world.CreateBody(def);
                var rect:b2PolygonDef = new b2PolygonDef();
                rect.SetAsBox(o.w, o.h);
                rect.restitution = 0.4;
                rect.friction = 0.1;
                body.CreateShape(rect);
                body.SetUserData({w:o.w, h:o.h});
                list.push(body);
            }
            return list;
        }

        /** [B2D]動く障害物をつくる
         * @return 障害物リスト
         */
        private function createFloat():Array {
            var a:Array = [
                {x:283, y:341, r:8},
                {x:112, y:178, r:8},
                {x:355, y:654, r:8},
                {x:170, y:473, r:8},
            ];
            var list:Array = [];
            for each (var o:* in a) {
                var def:b2BodyDef = new b2BodyDef();
                def.position.Set(o.x, o.y);
                def.linearDamping = 1.0;
                def.angularDamping = 1.0;
                var body:b2Body = world.CreateBody(def);
                var circle:b2CircleDef = new b2CircleDef();
                circle.density = 0.01;
                circle.radius = o.r;
                circle.restitution = 0.6;
                circle.friction = 0.2;
                body.CreateShape(circle);
                body.SetMassFromShapes();
                body.SetUserData({r:o.r});
                list.push(body);
            }
            return list;
        }

        /** 壁を表示
         */
        private function createFixedView():void {
            for each (var b:b2Body in fixed) {
                var o:* = b.GetUserData();
                var shape:Shape = new Shape();
                shape.graphics.lineStyle(1, 0x000000);
                shape.graphics.beginFill(0xCCCCCC);
                shape.graphics.drawRect(-o.w, -o.h, 2*o.w, 2*o.h);
                shape.graphics.endFill();
                var p:b2Vec2 = b.GetPosition();
                shape.x = p.x;
                shape.y = p.y;
                shape.rotation = b.GetAngle() * (180 / Math.PI);
                o.s = shape;
                worldView.addChild(shape);
            }
        }

        /** 障害物を作成
         */
        private function createFloatView():void {
            for each (var b:b2Body in float) {
                var o:* = b.GetUserData();
                var shape:Shape = new Shape();
                shape.graphics.lineStyle(1, 0x000000);
                shape.graphics.beginFill(0x444444);
                shape.graphics.drawCircle(0, 0, o.r);
                shape.graphics.endFill();
                var p:b2Vec2 = b.GetPosition();
                shape.x = p.x;
                shape.y = p.y;
                o.s = shape;
                worldView.addChild(shape);
            }
        }

        /** 障害物の位置を更新
         */
        private function updateFloatView():void {
            for each (var b:b2Body in float) {
                var o:* = b.GetUserData();
                var shape:Shape = o.s;
                var p:b2Vec2 = b.GetPosition();
                shape.x = p.x;
                shape.y = p.y;
                shape.rotation = b.GetAngle() * (180 / Math.PI);
            }
        }

        /** 背景を作成
         */
        private function createBackground():void {
            for each (var j:int in [START_Y, GOAL_Y]) {
                var shape:Shape = new Shape();
                var k:int = 0;
                for (var i:int = 0; i < W; i += 8) {
                    shape.graphics.beginFill(0xFFFFFF);
                    shape.graphics.drawRect(i, k, 8, 8);
                    shape.graphics.endFill();
                    k = k == 0 ? 8 : 0;
                }
                shape.x = 0;
                shape.y = j;
                worldView.addChild(shape);
            }
        }

        /** 物理エンジンの1ステップを実行します
         */
        private function step():void {
            world.Step(1 / 9, 10);
            // 車の位置
            var p:b2Vec2 = car.GetPosition(); // 車の中心
            var t:Number = car.GetAngle(); // 0が上向き
            var vy:Number =-Math.cos(t); // (vx,vy)進行方向ベクトル
            var vx:Number = Math.sin(t);
            // アクセル
            if (acl) {
                // 力を働かせる点は車の後方
                var rp:b2Vec2 = new b2Vec2(p.x - 16 *vx, p.y - 16 *vy);
                car.ApplyForce(new b2Vec2(A_FORCE *vx, A_FORCE *vy), rp);
            }
            // ブレーキ
            car.m_linearDamping = brk ? B_DAMP : NB_DAMP;
            // ステアリング
            if (steering != 0) {
                // 力を働かせる点は車の前方
                var sy:Number = S_FORCE *-Math.cos(t + steering);
                var sx:Number = S_FORCE * Math.sin(t + steering);
                var fp:b2Vec2 = new b2Vec2(p.x + 16 *vx, p.y + 16 *vy);
                car.ApplyForce(new b2Vec2(sx, sy), fp);
            }
            // モデルをビューに反映する
            var d:DisplayObject = DisplayObject(car.GetUserData());
            d.x = p.x;
            d.y = p.y;
            d.rotation = car.GetAngle() * (180 / Math.PI);
            updateFloatView();
            // スクロール
            //worldView.x = -(p.x - VW/2);
            worldView.y = -(p.y - VH/2);
            // 時間計測
            countup = (GOAL_Y <= p.y && p.y < START_Y);
        }

        private var _countup:Boolean = false;
        internal function get countup():Boolean { return _countup; }
        internal function set countup(newValue:Boolean):void {
            var oldValue:Boolean = this._countup;
            this._countup = newValue;
            if (!oldValue && newValue) { // false→true
                time = getTimer();
            }
        }
    }
}