Beautifl - Flash Gallery

Preview

Marimo Jump
tail_y 2009年8月28日 MIT License
?
      // forked from paq's Marimoを水に。
// forked from tail_y's Marimo on ground
// forked from tail_y's Marimo
// forked from clockmaker's Marimo with Pre-Rendering
package {
    import flash.display.*;
    import flash.events.Event;
    import flash.geom.Matrix;
    import jp.progression.commands.*;
    public class Marimo extends Sprite {
        // 空飛ぶマリモ的な物を作る。
        // 実際の構造は、マリモというか、やわらかいウニ。
        // 球形の表面からパーティクルを飛ばして、それを線で繋ぐ。
        // 問題点:線のつなぎ目が汚い 毛の長さが重力によって伸びてしまう ちょっと重い
        // できそうなこと:ビットマップにピクセルで描いて縮小するようにすれば、軽くなるかも。パーティクルを制御すれば、地面と毛が接する表現になるかも

                // 090526 tail_y
                // 衝突判定の説明のため、ぶっつけで実装。みんながfolkしてくれたノウハウは活かしていない。
                // 見た目のために値を色々調節してはいるが、基本的に影の描画順序を奥にしたのと、drawFurに3行追加しただけ。
        
        public static const STAGE_W:uint = 465;
        public static const STAGE_H:uint = 465;
        
        public static const SPHERE_R:Number = 20;        // マリモの体の(毛以外の部分の)半径。
    
        public static const PARTICLE_NUM_RATE:uint = 60;    // 球体の半円を分割する数。
        public static const PARTICLE_STEP:uint = 10;    // 線の描画回数。大きいほど長くなる。
        public static const PARTICLE_V:uint = 6;    // 線の勢い。大きいほど長く、粗くなる。
        public static const GRAVITY:Number = 0.3;    // 重力。毛の長さ補正をしていないので、重力が強すぎると毛が伸びる    
        public static const RANDOM_RATE:Number = 0.8;    // バラつき具合        
        public static const COLOR_RANDOM_RATE:Number = 0.3;    // 色のバラつき具合        
        
        public static const GROUND_Y:Number = 300;    // 地面位置    
        public static const SHADOW_W:Number = 170;    // 影サイズ
        public static const SHADOW_H:Number = 45;    // 影サイズ
        public static const GROUND_H:Number = 40;    // 影サイズ
        
        private var _marimoX:Number = STAGE_W/2;    // マリモの位置
        private var _marimoY:Number = STAGE_H/2 + 40;
        private var _display:Bitmap;
        private var _display2:Sprite;
        
        public static const COLOR_TIP_TOP:Number     = 0x77cc44;        // 毛先上部
        public static const COLOR_TIP_BOTTOM:Number  = 0x339900;        // 毛先下部
        public static const COLOR_BACE_TOP:Number    = 0x337711;        // 本体上部
        public static const COLOR_BACE_BOTTOM:Number = 0x000000;        // 本体下部

        public static const PRE_RENDER_NUM:uint = 30;
        private var gravity:Number = GRAVITY; // 重力変動
        private var progressBar:Sprite;
        private var renderCnt:int = 0;
        
        public function Marimo() {
    
             // キャプチャを40秒遅らせます
             Wonderfl.capture_delay( 40 );
                        
            stage.frameRate = 24;
            stage.quality = StageQuality.MEDIUM;
            
            // 準備
            _display = new Bitmap()
            addChild(_display)
            _display2 = new Sprite();
            
            
            // このあたりからカスタマイズ
            var bmpDataArr:Array = []
            var easeArr:Array = []
            var dummy:Sprite = new Sprite
            
            // Progressionのコマンドを使う
            var com:SerialList = new SerialList()
            com.addCommand(
                // イージングの値を取り出すためにダミーのTweenerを使う
                new DoTweener(dummy, {
                    x:400,
                    time:2,
                    onUpdate : function():void {
                        easeArr.push(dummy.x)
                    },
                    transition : "easeInSine"
                },{target: { x: -80 }} ),
                // プレレンダリング
                function():void {
                    
                    // ランダムな数値を先につくっておく
                    createPreRandom()
                    
                    // プログレスバー
                    progressBar = new Sprite()
                    progressBar.graphics.beginFill(0x0)
                    progressBar.graphics.drawRect(0, stage.stageHeight / 2, stage.stageWidth, 10)
                    progressBar.scaleX = 0
                    addChild(progressBar)
                    
                    // プレレンダリング処理
                    for (var i:int = 0; i < PRE_RENDER_NUM; i++) 
                    {
                        this.parent.insertCommand(
                            function():void {
                                drawMarimo()
                                var bmpData:BitmapData = new BitmapData(STAGE_W, STAGE_H, true, 0x00ffffff)
                                bmpData.draw(_display2)
                                bmpDataArr.push(bmpData)
                                progressBar.scaleX += 1 / PRE_RENDER_NUM;
                                
                                var ease:Number = easeArr[Math.round(renderCnt / PRE_RENDER_NUM * easeArr.length)] / 100
                                gravity = - GRAVITY * ease
                                _marimoY = STAGE_H / 3 + 32 * ease
                                
                                renderCnt++
                            },
                            // 非同期処理にしないと15秒スクリプト実行エラーがでてしまうんで
                            new Wait(50)
                        )
                    }
                },
                // レンダリング終了後に表示処理
                function():void {
                    removeChild(progressBar)
                    _display2.graphics.clear()
                    

                    // 影
                    var shadow:Sprite = new Sprite();
                    var g:Graphics = shadow.graphics;
                    addChildAt(shadow, 0);

                    var gradientMatrix:Matrix = new Matrix();
                    gradientMatrix.createGradientBox(SHADOW_W, GROUND_H, 0, _marimoX - SHADOW_W/2, GROUND_Y-GROUND_H/2);
                    g.beginGradientFill(GradientType.RADIAL, [0x000000, 0x000000], [0.3, 0.0], [60, 255], gradientMatrix);
                    g.drawRect(0, 0, STAGE_W, STAGE_H);
                    g.endFill();

                    
                    // エンターフレーム
                    var cnt:int = 0
                    var dir:int = 1
                                        var waitCount:int = 3;
                    addEventListener(Event.ENTER_FRAME, function():void {
                        cnt += dir
                        if (cnt == 1) dir = 1
                        if (cnt == PRE_RENDER_NUM - 1){
                                                    waitCount--;
                                                    dir = 0;
                                                    if (waitCount < 0){
                                                        dir = -1;
                                                        waitCount = 3;
                                                    }
                                                }
                        _display.bitmapData = bmpDataArr[cnt]
                        shadow.alpha = cnt / PRE_RENDER_NUM * .8 + .2
                    })

                    // ついでに背景の作成 不要なら以下4行を削除ください
                    var bgMatrix:Matrix = new Matrix()
                    bgMatrix.createGradientBox(STAGE_W, STAGE_H, Math.PI / 2, STAGE_W / 2, STAGE_H / 2)
                    graphics.beginGradientFill(GradientType.LINEAR, [0xFFFFFF, 0x888888], [1, 1], [0, 255], bgMatrix)
                    graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight)
                }
            
            )
            com.execute()
        }
        
        /**
         * ランダムな値もあらかじめ計算して配列に格納しておく
         */
        private var vxArr:Array = [];
        private var vyArr:Array = [];
        private function createPreRandom():void {
            for (var xri:uint = 0; xri < PARTICLE_NUM_RATE; xri++){    // zを先に計算して、奥から手前へ描画
                var xAngle:Number = Math.PI*xri / PARTICLE_NUM_RATE;    // マリモ中心点から、マリモ表面へのx方向に対する角度
                var z:Number = Math.cos(xAngle) * SPHERE_R;                // パーティクルを飛ばす原点Z
                var r:Number = Math.sin(xAngle) * SPHERE_R;                // その時の、断面の半径
                
                // z方向に向いた面の円周上にある点の数。円周の比(=半径の比)で割り出すが、整数に丸めるので、正確ではない。
                // (右側でそろっちゃうのが気持ち悪い。ランダムでz回転させてもいいかも。)
                var particleRateZ:int = PARTICLE_NUM_RATE * 2 * r / SPHERE_R;
                vxArr[xri] = []
                vyArr[xri] = []
                for (var zri:uint = 0; zri < particleRateZ; zri++){
                    // 方向をランダムでバラす。(本来は、円形方向へランダムにしなきゃいけないのだけど、面倒なのでこれで)
                    vxArr[xri][zri] = (PARTICLE_V * RANDOM_RATE) * (0.5 - Math.random());
                    vyArr[xri][zri] = (PARTICLE_V * RANDOM_RATE) * (0.5 - Math.random());
                }
            }
        }
        
        
        /**
         * まりもを描く処理
         */
        private function drawMarimo():void {
            // 描画
            var g:Graphics = _display2.graphics;
            
            // 本体
            g.clear();
            g.beginFill(COLOR_BACE_BOTTOM);
            g.drawCircle(_marimoX, _marimoY, SPHERE_R);
            g.endFill();
            
            // 毛
            for (var xri:uint = 0; xri < PARTICLE_NUM_RATE; xri++){    // zを先に計算して、奥から手前へ描画
                var xAngle:Number = Math.PI*xri / PARTICLE_NUM_RATE;    // マリモ中心点から、マリモ表面へのx方向に対する角度
                var z:Number = Math.cos(xAngle) * SPHERE_R;                // パーティクルを飛ばす原点Z
                var r:Number = Math.sin(xAngle) * SPHERE_R;                // その時の、断面の半径
                
                // z方向に向いた面の円周上にある点の数。円周の比(=半径の比)で割り出すが、整数に丸めるので、正確ではない。
                // (右側でそろっちゃうのが気持ち悪い。ランダムでz回転させてもいいかも。)
                var particleRateZ:int = PARTICLE_NUM_RATE * 2 * r / SPHERE_R;
                
                for (var zri:uint = 0; zri < particleRateZ; zri++){    // z方向面に対して、時計回りに描画
                    var zAngle:Number = Math.PI*zri*2 / particleRateZ;    // マリモ中心点から、マリモ表面へのz方向に対する角度
                    var x:Number = Math.cos(zAngle) * r;    // パーティクルを飛ばす原点X
                    var y:Number = Math.sin(zAngle) * r;    // パーティクルを飛ばす原点Y
                    var vx:Number = PARTICLE_V * x / SPHERE_R;    // パーティクルの速度X
                    var vy:Number = PARTICLE_V * y / SPHERE_R;    // パーティクルの速度Y
                    // 方向をランダムでバラす。(本来は、円形方向へランダムにしなきゃいけないのだけど、面倒なのでこれで)
                    vx += vxArr[xri][zri];
                    vy += vyArr[xri][zri];
                    
                    // 色を決める。上下の位置と、根本までの割合で色を決定する。ランダムもちょっと入れておく。
                    var yColorRate:Number = ((SPHERE_R + y)/ 2 / SPHERE_R) + COLOR_RANDOM_RATE * (0.5 - Math.random());
                    var color0:Number = mixColor(COLOR_BACE_TOP, COLOR_BACE_BOTTOM, yColorRate);
                    var color1:Number = mixColor(COLOR_TIP_TOP, COLOR_TIP_BOTTOM, yColorRate);
                    
                    // 毛
                    drawFur(_marimoX + x, _marimoY + y, vx, vy, color0, color1);
                }
            }
        }
        
        // 毛を描く
        private function drawFur(x:Number, y:Number, vx:Number, vy:Number, color0:Number, color1:Number):void{
            var lastX:Number;
            var lastY:Number;
            for (var i:uint=0; i<PARTICLE_STEP; i++){
                lastX = x;
                lastY = y;
                vy += GRAVITY;
                if (GROUND_Y-5 < y){    // この実装は本来正しくなく、z方向を使った計算をすべきだけど、そもそも元が3Dで計算してないので、とりあえずこれで。
                    vy*=0.7;
                }
                x += vx;
                y += vy;
                drawLine(lastX, lastY, x, y, mixColor(color0, color1, i/PARTICLE_STEP), 1 - i/PARTICLE_STEP);
            }
        }
        
        // 線を引く(ピクセルを打つ方法と、どっちが軽いかな)
        private function drawLine(x0:Number, y0:Number, x1:Number, y1:Number, color:uint, alpha:Number):void{
            var g:Graphics = _display2.graphics;
            g.lineStyle(1, color, alpha);
            g.moveTo(x0, y0);
            g.lineTo(x1, y1);
        }
        
        // 2つの色を指定した割合で混ぜた色を返す(rate=0ならcolor0)。(ここもかなり軽量化できるはず。グラデーションをキャッシュとして使うとか)
        private function mixColor(color0:uint, color1:uint, rate:Number):uint{
            if (rate <= 0) return color0;
            if (rate >= 1) return color1;
            var r:uint = (color0>>16) * (1-rate) 
                            + (color1>>16) * rate;
            var g:uint = ((color0 & 0x00ff00 ) >>8) * (1-rate) 
                            + ((color1 & 0x00ff00 ) >>8) * rate;
            var b:uint = (color0 & 0xff) * (1-rate) 
                            + (color1 & 0xff) * rate;
             return (r << 16) | (g << 8) | (b);
        }
        
    }
}