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

forked from: Particle Test

AS を始めたら必ず通るのが Particle だと勝手に思ってます。
ということで挑戦してみました!
偉大なる先人の方々を参考にさせて頂きました。ありがとうございました!
Get Adobe Flash player
by son1 13 Nov 2010
/**
 * Copyright son1 ( http://wonderfl.net/user/son1 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/fdXg
 */

// forked from kkeisuke's Particle Test
package  
{
    import caurina.transitions.Tweener;
    import com.bit101.components.CheckBox;
    import com.bit101.components.HSlider;
    import com.bit101.components.Label;
    import com.bit101.components.RadioButton;
    import net.hires.debug.Stats
    import flash.display.Shape;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    
    
    /** 
    * AS を始めたら必ず通るのが Particle だと勝手に思ってます。
    * ということで挑戦してみました!
    * 偉大なる先人の方々を参考にさせて頂きました。ありがとうございました!
    */
    [SWF(backgroundColor = 0xffffff, frameRate = 40, width = 465, height = 465)]
    public class ParticleTest extends Sprite
    {
        private var halfW:Number;
        private var halfH:Number;
        
        private var emitter:Emitter;
        private var emitters:/*Emitter*/Array = [];
        private var shapes:/*Shape*/Array;
        private var btns:/*RadioButton*/Array = [];
        
        private var current:int = 0;
        private var isAlpha:Boolean = true;
        private var num:int = 100;
        
        private var timer:Timer;
        
        private var labelHSlider:Label;
        
        
        public function ParticleTest() 
        {
            init();
        }
        
        
        private function init():void
        {
            halfW = stage.stageWidth * 0.5;
            halfH = stage.stageHeight * 0.5;
            
            // パーティクルの素材を4つ作成
            var bubble:Shape = new Shape();
            bubble.name = "Bubble";
            bubble.graphics.beginFill(0x0DADFC);
            GraphicsUtil.donuts(bubble.graphics, 5, 10, 0, 360);
            bubble.graphics.endFill();
            bubble.x = stage.stageWidth - bubble.width * 0.5 - 5;
            bubble.y = bubble.height * 0.5 + 5;
            this.addChild(bubble);
            
            var star:Shape = new Shape();
            star.name = "Star";
            star.graphics.lineStyle(1, 0xFCC00D);
            star.graphics.beginFill(0xFCE80D);
            GraphicsUtil.star(star.graphics, 6, 12);
            star.graphics.endFill();
            star.x = stage.stageWidth + star.width * 0.5 + 5;
            star.y = star.height * 0.5 + 5;
            this.addChild(star);
            
            var fire:Shape = new Shape();
            fire.name = "Fire";
            GraphicsUtil.drop(fire.graphics, 20, function():void { fire.graphics.beginFill(0xFC350D); } );
            fire.graphics.endFill();
            fire.x = stage.stageWidth + fire.width * 0.5 + 5;
            fire.y = fire.height * 0.5 + 5;
            this.addChild(fire);
            
            var snow:Shape = new Shape();
            snow.name = "Snow";
            snow.graphics.beginFill(0x0DE8FC);
            GraphicsUtil.star(snow.graphics, 6, 12, 6);
            snow.graphics.endFill();
            snow.x = stage.stageWidth + snow.width * 0.5 + 5;
            snow.y = snow.height * 0.5 + 5;
            this.addChild(snow);
            
            shapes = [bubble, star, fire, snow];
            
            var stats:Stats = new Stats();
            this.addChild(stats);
            
            // パーティクルを表示する ParticleField
            var field:ParticleField = new ParticleField(stage.stageWidth, stage.stageHeight, 0x00000000);
            this.addChildAt(field, 0);
            
            var n:int = shapes.length;
            for (var i: int = 0; i < n; i++) 
            {
                // 表示場所と各素材を持った Emitter
                emitters[i] = new Emitter(field, shapes[i]);
                // 表示しているパーティクルがなくなった時
                emitters[i].addEventListener(Event.COMPLETE , complete);
                
                if (i == 0) 
                {
                    var h:int = stats.height + 10;
                    
                }else 
                {
                    h = btns[i - 1].y + btns[i - 1].height + 10;
                }
                
                btns[i] = new RadioButton(this, 5, h, shapes[i].name, false, onRadioButtonClick);
                btns[i].name = i;
            }
            
            var checkBox:CheckBox = new CheckBox(this, 5, btns[btns.length-1].y + btns[btns.length-1].height + 10, "Alpha " + isAlpha, onCheckBoxClick);
            checkBox.selected = true;
            
            var hSlider:HSlider = new HSlider(this, 5, checkBox.y + checkBox.height + 10);
            hSlider.setSize(60, 10);
            hSlider.setSliderParams(num, 800, num);
            hSlider.addEventListener(Event.CHANGE, onHSliderChange);
            
            labelHSlider = new Label(this, 5, hSlider.y + hSlider.height + 2);
            labelHSlider.text = "Particle's " + hSlider.value;
            
            // 初期
            emitter = emitters[0];
            btns[0].selected = true;
            
            // 100ms 毎に Emitter から パーティクルを作る
            timer = new Timer(100);
            timer.addEventListener(TimerEvent.TIMER , createParticle);
            timer.start();
            
            // Emitter を更新して、描画する
            this.addEventListener(Event.ENTER_FRAME , update);
        }
        
        
        private function createParticle(e:TimerEvent):void 
        {
            var mx:Number = mouseX;
            var my:Number = mouseY;
            
            var dmx:Number = (mx - (halfW)) / halfW;
            var dmy:Number = (my - (halfH)) / halfH;
            
            // BitmapData.colorTransform() で 3倍弱遅くなる
            if (isAlpha) 
            {
                var setAlpha:Function = function ():Number { return Math.random() * 0.4 + 0.6 };
                var vAlpha:Number = -0.002;
                
            }else 
            {
                setAlpha = function ():int { return 1 };
                vAlpha = 0;
            }
            
            // 個数
            var n:int = num;
            while (n--)
            {
                // Emitter から パーティクルを作る
                var particle:Particle = emitter.generate();
                // 個々の動きの設定
                particle.ax = - dmx;
                particle.ay = - dmy;
                //particle.life = 40;
                particle.alpha = setAlpha();
                particle.vAlpha = vAlpha;
                //particle.friction = 0.95;
                particle.vx = Math.random() * 16 - 8;
                particle.vy = Math.random() * 16 - 8;
                particle.x = mx + Math.random() * 32 - 16;
                particle.y = my + Math.random() * 32 - 16;
            }
        }
        
        
        private function update(e:Event):void 
        {
            // Emitter を更新して、描画する
            emitter.update();
        }
        
        
        private function complete(e:Event):void 
        {
            Tweener.addTween(shapes[current], { x:stage.stageWidth - shapes[current].width * 0.5 - 5, time:1, transition:"easeOutExpo" } );
            
            emitter = emitters[current];
            
            timer.start();
        }
        
        
        private function onRadioButtonClick(e:MouseEvent):void
        {
            timer.stop();
            
            Tweener.addTween(shapes[current], { x:stage.stageWidth + shapes[current].width * 0.5 + 5, time:1, transition:"easeOutExpo" } );
            
            current = int(e.currentTarget.name);
        }
        
        
        private function onCheckBoxClick(e:MouseEvent):void
        {
            timer.stop();
            
            isAlpha = !isAlpha;
            
            e.currentTarget.label = "Alpha " + isAlpha;
        }
        
        
        private function onHSliderChange(e:Event):void 
        {
            var n:int = e.currentTarget.value >> 0;
            
            labelHSlider.text = "Particle's " + String(n);
            num = n;
        }
        
    }

}



import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.Event;


/** 
* パーティクルを描画する土台
*/
class ParticleField extends Sprite
{
    private var _canvas:BitmapData;
    
    private var w:int;
    private var h:int;
    private var _bgColor:uint;
    
    
    /** 
    * コンストラクタ
    * @param w 幅
    * @param h 高さ
    * @param _bgColor 背景色
    */
    public function ParticleField(w:int, h:int, _bgColor:uint)
    {
        this.w = w;
        this.h = h;
        this._bgColor = bgColor;
        
        init();
        
        this.addEventListener(Event.ADDED_TO_STAGE , setStage);
    }
    
    
    private function setStage(e:Event):void 
    {
        removeEventListener(Event.ADDED_TO_STAGE, setStage);
        
        // パーティクルクラスの初期化
        Particle.init(stage);
    }
    
    
    private function init():void
    {
        canvas = new BitmapData(w, h, true, _bgColor);
        var canvasBitMap:Bitmap = new Bitmap(canvas);
        canvasBitMap.smoothing = true;
        this.addChild(canvasBitMap);
    }
    
    
    /** 
    * パーティクルを描画している BitmapData
    */
    public function get canvas():BitmapData { return _canvas; }
    
    public function set canvas(value:BitmapData):void 
    {
        _canvas = value;
    }
    
    /** 
    * 背景色
    */
    public function get bgColor():uint { return _bgColor; }
    
}



import flash.display.BitmapData;
import flash.display.IBitmapDrawable;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;


/** 
* パーティクル、素材とその土台を管理するクラス
*/
class Emitter extends EventDispatcher
{
    // BitmapData に draw できるもの
    private var material:IBitmapDrawable;
    
    private var bmd:BitmapData;
    
    private var scalable:Boolean;
    private var num:int = 5;
    private var bmds:/*BitmapData*/Array = [];
    
    private var particles:/*Particle*/Array = [];
    private var field:ParticleField;
    
    private var point:Point;
    private var canvas:BitmapData;
    private var rect:Rectangle;
    private var bgColor:uint;
    
    
    /** 
    * コンストラクタ
    * @param field パーティクルを表示する土台
    * @param material パーティクルの素材
    * @param scalable パーティクルのスケールを有効化するか
    */
    public function Emitter(field:ParticleField, material:IBitmapDrawable, scalable:Boolean = true)
    {
        this.field = field;
        this.material = material;
        this.scalable = scalable;
        
        init();
    }
    
    
    private function init():void
    {
        // あらかじめ、スケールが 0.2~1 までの大きさのコピーを作っておく。
        // Particle に scale が無いので、これで代替。
        // 傾きを今後どうするか。
        if (scalable)
        {
            for (var i: int = 0; i < num; i++) 
            {
                var w:Number = material["width"] / num * (i + 1);
                var h:Number = material["height"] / num * (i + 1);
                
                bmds[i] = new BitmapData(w, h, true, 0x00000000);
                
                var mtx:Matrix = new Matrix();
                mtx.scale(1 / num * (i + 1), 1 / num * (i + 1));
                mtx.translate(w * 0.5, h * 0.5);
                
                bmds[i].draw(material, mtx);
            }
            
        }else 
        {
            w = material["width"];
            h = material["height"];
            
            bmd = new BitmapData(w, h, true, 0x00000000);
            mtx = new Matrix();
            mtx.translate(w * 0.5, h * 0.5);
            
            bmd.draw(material, mtx);
        }
        
        // update で使うものをあらかじめ定義してみる。
        this.point = new Point();
        this.canvas = field.canvas;
        this.rect = canvas.rect;
        this.bgColor = field.bgColor;
    }
    
    
    /** 
    * パーティクルを作成する
    * @return Particle パーティクル
    */
    public function generate():Particle 
    {
        // スケールが有効ならば配列から。そうでなければ、単体で。
        if (scalable) 
        {
            // >> 0 は、int() キャストと同じ。
            // >> 0 の前を()で囲むと、ちょっと早くなった気がする。気のせいかな。
            var particle:Particle = new Particle(bmds[(Math.random() * num) >> 0]);
            
        }else 
        {
            particle = new Particle(bmd);
        }
        
        particles.push(particle);
        
        return particle;
    }
    
    
    /** 
    * Emitter を更新して、描画する
    */
    public function update():void 
    {
        var i:int = particles.length;
        
        if (i <= 0)
        {
            // パーティクルがなくなった時
            dispatchEvent(new Event(Event.COMPLETE));
        }
        
        canvas.lock();
        
        // fillRect より早い方法は??
        canvas.fillRect(rect, bgColor);
        
        while (i--) 
        {
            // ローカル変数で持ったほうがデータ型が指定できて早い?
            var particle:Particle = particles[i];
            var pbmd:BitmapData = particle.bmd;
            
            // 1個のパーティクルの更新と、生存しているか。
            var life:Boolean = particle.update();
            
            if (life) 
            {
                // 描画
                point.x = particle.x;
                point.y = particle.y;
                field.canvas.copyPixels(pbmd, pbmd.rect, point, null, null, true);
                
            }else 
            {
                // 削除
                particles.splice( i, 1 );
            }
        }
        
        canvas.unlock();
    }
    
}



import flash.display.BitmapData;
import flash.display.Stage;
import flash.geom.ColorTransform;
import flash.geom.Rectangle;


/** 
* パーティクルの性質を保持する
*/
class Particle
{
    // rote と scale がない。
    private var _ax:Number = 0;
    private var _ay:Number = 0;
    private var _vx:Number = 0;
    private var _vy:Number = 0;
    private var _friction:Number = 1;
    private var _x:Number = 0;
    private var _y:Number = 0;
    
    private var _vAlpha:Number = 0;
    private var _alpha:Number = 1;
    private var colorTransFrom:ColorTransform;
    
    private var _life:int = 40;
    private var _isLife:Boolean = true;
    
    private var _bmd:BitmapData;
    private var rect:Rectangle;
    
    private static var sw:int;
    private static var sh:int;
    
    
    /** 
    * コンストラクタ。Emitter から生成される。
    * @param _bmd 実際に描画する BitmapData
    */
    public function Particle(_bmd:BitmapData)
    {
        this._bmd = _bmd.clone();
        
        sets();
    }
    
    
    /** 
    * 初期化。パーティクルがステージ内にあるかを調べるために Stage が必要。
    * ParticleField が呼び出す。
    * @param s ステージ
    */
    public static function init(s:Stage):void 
    {
        sw = s.stageWidth;
        sh = s.stageHeight;
    }
    
    
    private function sets():void
    {
        colorTransFrom = new ColorTransform();
        rect = _bmd.rect;
    }
    
    
    /** 
    * パーティクルの状態を更新。Emitter が更新する。
    * @return Boolean パーティクルが生存しているか
    */
    public function update():Boolean 
    {
        if (_isLife) 
        {
            // ステージの範囲とalpha
            if ((_x < 0 || _x > sw) || (_y < 0 || _y > sh) || _alpha <= 0)
            {
                remove();
                return _isLife;
            }
            
            _life--;
            
            if (_life <= 0)
            {
                remove();
                return _isLife;
            }
            
            _x += _vx;
            _y += _vy;
            _vx += _ax;
            _vy += _ay;
            _vx *= _friction;
            _vy *= _friction;
            
            // 配置座標を整数化 → 1.25倍くらい
            _x = _x >> 0;
            _y = _y >> 0;
            
            // alpha を BitmapData に適用
            if (_vAlpha != 0) 
            {
                _alpha += _vAlpha;
                colorTransFrom.alphaMultiplier = _alpha;
                
                // これが重い!!
                _bmd.colorTransform(rect, colorTransFrom);
            }
        }
        
        return _isLife;
    }
    
    
    private function remove():void 
    {
        _life = 0;
        _isLife = !_isLife;
        
        // 念のため
        _bmd.dispose();
        _bmd = null;
    }
    
    
    /** 
    * X軸方向への加速度
    */
    public function get ax():Number { return _ax; }
    
    public function set ax(value:Number):void 
    {
        _ax = value;
    }
    
    /** 
    * Y軸方向への加速度
    */
    public function get ay():Number { return _ay; }
    
    public function set ay(value:Number):void 
    {
        _ay = value;
    }
    
    /** 
    * X軸方向への速度
    */
    public function get vx():Number { return _vx; }
    
    public function set vx(value:Number):void 
    {
        _vx = value;
    }
    
    /** 
    * Y軸方向への速度
    */
    public function get vy():Number { return _vy; }
    
    public function set vy(value:Number):void 
    {
        _vy = value;
    }
    
    /** 
    * X軸上の座標
    */
    public function get x():Number { return _x; }
    
    public function set x(value:Number):void 
    {
        _x = value >> 0;
    }
    
    /** 
    * Y軸上の座標
    */
    public function get y():Number { return _y; }
    
    public function set y(value:Number):void 
    {
        _y = value >> 0;
    }
    
    /** 
    * 透過度の変化値
    */
    public function get vAlpha():Number { return _vAlpha; }
    
    public function set vAlpha(value:Number):void 
    {
        _vAlpha = value;
    }
    
    /** 
    * 透過度
    */
    public function get alpha():Number { return _alpha; }
    
    public function set alpha(value:Number):void 
    {
        _alpha = value;
        
        if (_alpha < 1) 
        {
            colorTransFrom.alphaMultiplier = _alpha;
            _bmd.colorTransform(_bmd.rect, colorTransFrom);
        }
    }
    
    /** 
    * 抵抗
    */
    public function get friction():Number { return _friction; }
    
    public function set friction(value:Number):void 
    {
        _friction = value;
    }
    
    /** 
    * 生存値
    */
    public function get life():int { return _life; }
    
    public function set life(value:int):void 
    {
        _life = value;
    }
    
    /** 
    * 生存しているかどうか
    */
    public function get isLife():Boolean { return _isLife; }
    
    /** 
    * 実際に描画する BitmapData
    */
    public function get bmd():BitmapData { return _bmd; }
    
}



import flash.display.Graphics;
import flash.display.Shape;
import flash.geom.Point;


/** 
* 既存にない Graphics を描画するクラスです。Graphics を返します。
*/
class GraphicsUtil
{
    static private var PI:Number = Math.PI;
    static private var DEGTORAD:Number = Math.PI / 180;
    
    
    /**
      * ドーナツ形
      * @param g Graphics ターゲット 
      * @param sr Number 描画開始半径
      * @param er Number 描画終了半径
      * @param sa Number 描画開始角度
      * @param ea Number 描画終了角度
      * @return Graphics 生成したドーナツ形
      */
    public static function donuts(g:Graphics, sr:Number, er:Number, sa:Number, ea:Number):Graphics
    {
        var sap:Point = Point.polar(sr, sa * DEGTORAD);
        var eap:Point = Point.polar(er, ea * DEGTORAD);
        var ap:Point;
        var cp:Point;
        
        g.moveTo(sap.x, sap.y);
        
        for (var i:int = sa; i <= ea; i++)
        {
            ap = Point.polar(sr, i * DEGTORAD);
            cp = Point.polar(sr, (i - 0.5) * DEGTORAD);
            g.curveTo(cp.x, cp.y, ap.x, ap.y);
        }
        
        g.lineTo(eap.x, eap.y);
        
        for (i = ea; i >= sa; i--)
        {
            ap = Point.polar(er, i * DEGTORAD);
            cp = Point.polar(er, (i + 0.5) * DEGTORAD);
            g.curveTo(cp.x, cp.y, ap.x, ap.y);
        }
        
        return g;
    }
    
    
    /** 
    * 星形
    * @param g Graphics ターゲット 
    * @param inR 内側の半径
    * @param outR 外側の半径
    * @param vertex 頂点の数。5点以上
    * @return Graphics 生成した星形
    */
    public static function star(g:Graphics, inR:Number, outR:Number, vertex:int = 5):Graphics
    {
        var points:/*Number*/Array = [];
        var rad:Number = PI * 2 / vertex;
        
        for (var i: int = 0; i < vertex; i++) 
        {
            var outTheta:Number = (i * rad) - PI * 0.5;
            var inTheta:Number = outTheta + (rad * 0.5);
            
            points[i << 2] = outR * Math.cos(outTheta);
            points[(i << 2) + 1] = outR * Math.sin(outTheta);
            points[(i << 2) + 2] = inR * Math.cos(inTheta);
            points[(i << 2) + 3] = inR * Math.sin(inTheta);
        }
        
        g.moveTo(points[0], points[1]);
        
        var n:int = points.length >> 1;
        for (var j: int = 1; j < n; j++) 
        {
            g.lineTo(points[j << 1], points[(j << 1) + 1]);
        }
        
        g.lineTo(points[0], points[1]);
        
        return g;
    }
    
    
    /** 
    * 雫形
    * @param g Graphics ターゲット 
    * @param r 半径
    * @param fill 塗りを実行する関数
    * @return Graphics 生成した雫形
    */
    public static function drop(g:Graphics, r:int, fill:Function):Graphics
    {
        for (var i: int = 0; i < r; i++)
        {
            fill();
            g.drawCircle(0, (r >> 2) - i, (r - i) / 3);
        }
        
        return g;
    }
    
}