forked from: forked from: HANABI(初心者がアレンジしてみた)

by nayu
♥0 | Line 157 | Modified 2010-09-16 09:31:40 | MIT License
play

ActionScript3 source code

/**
 * Copyright nayu ( http://wonderfl.net/user/nayu )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/jXjR
 */

// forked from Uwitch's forked from: HANABI(初心者がアレンジしてみた)
// forked from yanbaka's HANABI
/*
 * 比較的短いコードでこれだけのことが出来ることに感動。
 * wonderflらしくForkしてアレンジに挑戦。
 * 解説付きがあったことは、初心者として多いに勉強になりました。
 * Hiiragi氏には感謝です。
 * 
 * 変更箇所のみコメントしてあります。
 * 
 * 【変更点】
 * ・花火の消え方の変更
 * ・色の変化を色相環を周回するように変更
 * ・時間経過で花火の発生間隔が変わるように変更
 */
package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.PixelSnapping;
    import flash.display.Sprite;
    import flash.events.Event;
    //import flash.events.TimerEvent;
    import flash.filters.BlurFilter;
    import flash.geom.ColorTransform;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    //import flash.utils.Timer;
    [SWF(width = "465", height = "465", backgroundColor = "0x000000", frameRate = "30")]
    
    public class Hanabi extends Sprite
    {
        private const WIDTH:Number = 465;
        private const HEIGH:Number = 465;
        
        //_glowの効果を確認するために定数に置き換えただけで、数値そのものはオリジナルからいじってません
        //要するに数値いじって遊びやすくしただけ
        private const GLOW:Number = 4;

        //花火の初期ディレイ(フレーム数指定)
        private const IniD:Number = 45;
        
        private var _particles:Array;
        private var _canvas:BitmapData;
        private var _glow:BitmapData;
        private var _rect:Rectangle;
        private var cTra:ColorTransform;
        
        //色相情報(0:赤,60:黄,120:緑,180:シアン,240:青,300:マゼンタ の6つに分類しただけで単なるフラグ)
        private var hue:int;

        //色の変化スピード(色相環を一周する時間:フレーム数指定)
        private var hueC:Number = 240;
        
        private var sx:Number;
        private var sy:Number;
        
        //ディレイ,花火カウント,フレームカウント用
        private var delay:Number = IniD;
        private var cnt:int;
        private var fcnt:uint;
        
        public function Hanabi()
        {
            init();
        }
        
        private function init():void
        {
            _particles = [];
            _canvas = new BitmapData(WIDTH, HEIGH, false, 0x0);
            addChild(new Bitmap(_canvas)) as Bitmap;
            
            _glow = new BitmapData(WIDTH/GLOW, HEIGH/GLOW, false, 0x0);
            var bm:Bitmap = addChild(new Bitmap(_glow, PixelSnapping.NEVER, true)) as Bitmap;
            bm.scaleX = bm.scaleY = GLOW;
            bm.blendMode = BlendMode.ADD;
            
            _rect = new Rectangle(0, 0, WIDTH, HEIGH);
            //色相の0に合わせて、Rを0.9倍、GとBを0.8倍に変更
            cTra = new ColorTransform(.9, .8, .8, 1.0);
            
            //秒⇒フレーム毎の増減値に変換
            hueC = (1 / (hueC / 6)) * 0.1;
           
            //最初に一発だけ打ち上げる
            hanabi();
            
            this.stage.addEventListener(Event.ENTER_FRAME, enterframeHandler);
            
            //Timerイベントの廃止
      }
        
        private function resetFunc():void
        {
            //Timerイベントからフレームイベントに変更
            //色の変化を色相環を周回しているっぽく変更
            //0.8⇔0.9と変化させる幅は変えず、RGBの値をシフトしながら増減させているだけ
            switch ( hue ) {
                case 0:
                    cTra.greenMultiplier += hueC;
                    break;
                case 60:
                    cTra.redMultiplier -= hueC;
                    break;
                case 120:
                    cTra.blueMultiplier += hueC;
                    break;
                case 180:
                    cTra.greenMultiplier -= hueC;
                    break;
                case 240:
                    cTra.redMultiplier += hueC;
                    break;
                case 300:
                    cTra.blueMultiplier -= hueC;
                    break;
                default :
            }
            if ( cTra.redMultiplier + cTra.greenMultiplier + cTra.blueMultiplier <= 2.5 || cTra.redMultiplier + cTra.greenMultiplier + cTra.blueMultiplier >= 2.6) {
                hue = ( hue + 60 ) % 360;
            }
            if ( hanabishoot() == true ) {
                hanabi();
            }
        }
        
        //花火打ち上げのタイミングでtrueを返す
        private function hanabishoot():Boolean {
            var flag:Boolean;
            if (delay <= fcnt++) {
                if (cnt == -1) {
                    delay = IniD;
                } else if (delay > 2) {
                    delay *= 0.96;
                } else {
                    delay = 2;
                }
                fcnt = 0;
                cnt ++;
                if (cnt >= 180) {
                    delay = 120;
                    cnt = -1;
                }
                flag = true;
            }
            return flag;
        }
              
        private function hanabi():void 
        {
            var i:int = 200;
            sx = Math.random()*WIDTH;
            sy = Math.random()*HEIGH/2;
            while (i--) createParticle();
        }
        
        private function createParticle():void {
            var p:Particle = new Particle();
            p.x = sx;
            p.y = sy;
            var radius:Number = Math.sqrt(Math.random())*10;
            var angle:Number = Math.random()*(Math.PI)*2;
            p.vx = Math.cos(angle) * radius;
            p.vy = Math.sin(angle) * radius;
            _particles.push(p);
        }
        
        private function enterframeHandler(e:Event):void
        {
            //resetFuncをこっちで呼び出すようにお引越し
            resetFunc();
            update();
        }
        
        private function update():void {
            _canvas.lock();
            _canvas.applyFilter(_canvas, _rect, new Point(), new BlurFilter(1, 1));
            _canvas.colorTransform(_rect, cTra);
            var i:int = _particles.length;
            while (i--) {
                var p:Particle = _particles[i];
                p.life --;    
                p.vy += 0.2;
                p.vx *= 0.9;
                p.vy *= 0.9;
                p.x += p.vx;
                p.y += p.vy;
                _canvas.setPixel32(p.x, p.y, p.c);
 
                //パーティクルがステージより上に出た場合は残すように変更
                //速度による除去を廃止し、寿命で消えるように変更
                if ((p.x > stage.stageWidth || p.x < 0) || p.life < 0)
                {
                    this._particles.splice(i, 1);
                }
            }
            _canvas.unlock();
            _glow.draw(_canvas, new Matrix(1/GLOW, 0, 0, 1/GLOW));
        }
    }
}

class Particle
{
    public var x:Number = 0;
    public var y:Number = 0;
    public var vx:Number = 0;
    public var vy:Number = 0;
    public var c:uint = 0xFFFFFFFF;
    //パーティクルの寿命(37~53フレーム)
    public var life:Number = 35 + Math.random() * 18;
    
    public function Particle() {}
}