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

炎エフェクト(Android検証用)

概要
・Androidで動的な炎がどの程度実用的なのか確認するためのコード

検証用メモ
・ラベルをクリックするとオンオフの切替が可能
・UseBlur
 ・ブラーを使うか否か
 ・ブラーを使うとより炎っぽくなり自然に見える
 ・基本的にブラーは重いはず
・UseSubtract
 ・パーリンノイズを加算で使うか減算で使うか
 ・OpenGLはベース部分では減算をサポートしてないので、減算だと遅くなるかもしれない

動作メモ(Desire HD)
・アプリ(ランチャー内のFireTest)
 ・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype
/**
 * Copyright o_healer ( http://wonderfl.net/user/o_healer )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/bYBE
 */

/*
 炎エフェクト(Android検証用)

 概要
 ・Androidで動的な炎がどの程度実用的なのか確認するためのコード

 検証用メモ
 ・ラベルをクリックするとオンオフの切替が可能
 ・UseBlur
  ・ブラーを使うか否か
  ・ブラーを使うとより炎っぽくなり自然に見える
  ・基本的にブラーは重いはず
 ・UseSubtract
  ・パーリンノイズを加算で使うか減算で使うか
  ・OpenGLはベース部分では減算をサポートしてないので、減算だと遅くなるかもしれない

 動作メモ(Desire HD)
 ・アプリ(ランチャー内のFireTest)
  ・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype
 ・全てオフにしたら十分な速度で動いた
  ・ただし、白バックの黒炎限定になるので使いどころが難しい
 ・ブラーをかけるだけでだいぶ重くなる
 ・減算にしても特に処理落ちしてなかった
  ・黒バックの炎になるので、これをさらにどこかに加算すれば良さそう

 参考
 ・以下のコードをさらに簡略化したもの
  ・http://wonderfl.net/c/dzI0
 ・さらに元をたどれば以下を参考にしたもの
  ・http://wonderfl.net/c/rolo/
*/

package {
    import flash.display.*;
    import flash.events.*;
    import flash.filters.*;
    import flash.geom.*;
    import flash.net.*;
    import flash.system.*;
    import flash.text.*;
    import flash.media.*;
 
    [SWF(width="465", height="465", frameRate="30", backgroundColor="0x000000")]
    public class FireTest extends Sprite {

        //==Const==

        //表示サイズ
        static public const VIEW_W:int = 465;
        static public const VIEW_H:int = 465;

        //テキスト兼ボタンを並べる際の間隔
        static public const TEXT_OFFSET_Y:int = 32;

        //Utility
        static public const VIEW_RECT:Rectangle = new Rectangle(0,0,VIEW_W,VIEW_H);
        static public const POS_ZERO:Point = new Point(0,0);


        //==Var==

        //#炎の描画&処理まわり

        //実際の表示に使うビットマップ
        public var m_BitmapData_View:BitmapData = new BitmapData(VIEW_W, VIEW_H, true, 0x00000000);
        public var m_Bitmap_View:Bitmap = new Bitmap(m_BitmapData_View);
        //炎の状態を0x00~0xFFで保持するデータ
        public var m_BitmapData_Fire:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x00);
        public var m_Bitmap_Fire:Bitmap = new Bitmap(m_BitmapData_Fire);
        //Fire => Viewの変換のためのパレット(0x00~0xFFの値を、実際の炎の色に置き換える)
//        public var m_Palette_Fire_to_View:Array = new Array(256);
        //減衰に使うためのパーリンノイズ
        public var m_BitmapData_PerlinNoise:BitmapData = new BitmapData(VIEW_W * 2, VIEW_H * 2, false, 0x000000);
        //スクロールに使うマトリクス
        public var m_Mtx_PerlinNoise:Matrix = new Matrix();
        //広げるためのブラーフィルター
        public var m_Filter_FireBlur:BlurFilter = new BlurFilter(4,4);

        //#発火部分の描画まわり

        static public const LINE_W:uint = 16;
        public var mouseX_Old:int = 0;
        public var mouseY_Old:int = 0;
        public var fireShape:Shape = new Shape();
        public var fireShape_Fix:Shape = new Shape();

        //#背景
        public var m_BitmapData_BG:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x000000);

        //#検証用フラグ
//        public var m_UsePalette:Boolean = false;
        public var m_UseBlur:Boolean = false;
        public var m_UseSubtract:Boolean = false;

        //#テキスト兼ボタン
        public var m_Text_UseBlur:TextField = new TextField();
        public var m_Text_UseSubtract:TextField = new TextField();


        //==Function==

        //Init
        public function FireTest(){
            if(stage != null){
                Init(null);
            }else{
                addEventListener(
                    Event.ADDED_TO_STAGE,//ステージに追加されたら
                    Init
                );
            }
        }
        public function Init(e:Event):void
        {
            var i:int;

//            //Settings
//            {
//                //画面幅に合わせ、長い方が画面にフィットするように拡大
//                //- 短い方は画面にフィットせず、もともと画面外だったものが内部表示されるかも
//                stage.scaleMode = StageScaleMode.SHOW_ALL;
//            }

            //m_BitmapData_PerlinNoise
            {
                //普通にパーリンノイズを生成して
                const PanelLen:int = 24;//火種のおおまかな大きさ(ドット絵に使うので、1マス=32ドットあたりを想定)
                const Octave:int = 2;//変化は雑でいい
                const stitch:Boolean = true;//端はループできる感じで
                const fractalNoise:Boolean = true;//なめらかな変化で
                const channel:uint = 7;//全てのチャンネルを使用して
                const grayScale:Boolean = true;//RGBは全て同じ値で
                m_BitmapData_PerlinNoise.perlinNoise(PanelLen,PanelLen, Octave, 1000*Math.random(), stitch, fractalNoise, channel, grayScale);
                //それを縦に2枚並べる形にして(スクロールするため。つなぎ目は気にしない)
                m_BitmapData_PerlinNoise.copyPixels(m_BitmapData_PerlinNoise, new Rectangle(0,0,VIEW_W*2,VIEW_H), new Point(0,VIEW_H));
                //減衰に使うため値を抑制
                const ratio:Number = 0.09;//小さくすると炎の伸びが大きくなる
                m_BitmapData_PerlinNoise.colorTransform(
                    m_BitmapData_PerlinNoise.rect,//VIEW_RECTとは範囲が違うので、直のrectを使う
                    new ColorTransform(ratio,ratio,ratio)//値を減衰させる
                );
            }
/*
            //m_Palette_Fire_to_View
            {
                for(i = 0; i < 256; i++){
                    //Cosによって発火部分と消える部分の境界を薄める
                    //さらにPowによって減衰の速さを調整する(白→黄色→橙になるように)
                    var r:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 1.0)));
                    var g:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 1.5)));
                    var b:uint = 0xFF * (0.5 - 0.5*Math.cos(Math.PI * Math.pow(i/255, 3.0)));

                    m_Palette_Fire_to_View[i] = (0xFF<<24)|(r<<16)|(g<<8)|(b<<0);
                }
            }
//*/
            //背景
            {
                addChild(new Bitmap(m_BitmapData_BG));
            }

            //炎の表示
            {
                //m_Bitmap_View.blendMode = BlendMode.ADD;//加算表現にすることによって、黒=透明として扱える

//                if(m_UsePalette){
//                    addChild(m_Bitmap_View);
//                }else{
                    addChild(m_Bitmap_Fire);
//                }
            }

            //マウスによる発火処理
            {
                stage.addEventListener(MouseEvent.MOUSE_MOVE,    DrawEmit);
                stage.addEventListener(MouseEvent.MOUSE_DOWN,    OnMouseDown);
            }

            //Update
            {
                addEventListener(Event.ENTER_FRAME, Update);
            }

            //Text
            {
                m_Text_UseBlur.selectable = false;
                m_Text_UseBlur.autoSize = TextFieldAutoSize.LEFT;
                m_Text_UseBlur.defaultTextFormat = new TextFormat('Verdana', 24, 0xFFFFFF, true);
                m_Text_UseBlur.text = '';
                m_Text_UseBlur.filters = [new GlowFilter(0x000000,1.0, 2,2)];
                m_Text_UseBlur.y = VIEW_H - 2*TEXT_OFFSET_Y;

                addChild(m_Text_UseBlur);
            }
            {
                m_Text_UseSubtract.selectable = false;
                m_Text_UseSubtract.autoSize = TextFieldAutoSize.LEFT;
                m_Text_UseSubtract.defaultTextFormat = new TextFormat('Verdana', 24, 0xFFFFFF, true);
                m_Text_UseSubtract.text = '';
                m_Text_UseSubtract.filters = [new GlowFilter(0x000000,1.0, 2,2)];
                m_Text_UseSubtract.y = VIEW_H - 1*TEXT_OFFSET_Y;

                addChild(m_Text_UseSubtract);
            }

            //Flag
            {
                Refresh_UseBlur();
                Refresh_UseSubtract();
            }

            //OnEnd
            {
                addEventListener(Event.REMOVED_FROM_STAGE, Finish);
            }

            //Test
//            Switch_UseSubtract();
        }

        //Finish
        public function Finish(e:Event):void{
            removeEventListener(Event.ADDED_TO_STAGE, Init);
            removeEventListener(Event.ENTER_FRAME, Update);
            removeEventListener(Event.REMOVED_FROM_STAGE, Finish);
        }

        //Update
        public function Update(e:Event=null):void{
            //var DeltaTime:Number = 1.0 / stage.frameRate;

            //発火部分の描画
            Emit();

            //炎表現
            DrawFire();
        }

        //Emit:マウスに応じて発火
        public function DrawEmit(e:MouseEvent):void{
            var color:uint = m_UseSubtract? 0xFF8800: 0x000000;
            if(e.buttonDown){//クリックされてる間だけ、固定発火の描画を行う
                var g:Graphics = fireShape_Fix.graphics;

                //マウスの動きに応じて線
                {
                    g.lineStyle(LINE_W,color,1.0);
                    g.moveTo(mouseX_Old, mouseY_Old);
                    g.lineTo(mouseX, mouseY);
                }
            }
        }
        public function Emit():void{
            var color:uint = m_UseSubtract? 0xFF8800: 0x000000;
            var g:Graphics = fireShape.graphics;

            //マウス位置に円
            {
                const RAD:uint = 8;

                g.lineStyle(0,0,0);
                g.beginFill(color, 1.0);
                g.drawCircle(mouseX, mouseY, RAD);
                g.endFill();
            }

            //マウスの動きに応じて線
            {
                g.lineStyle(LINE_W,color,1.0);
                g.moveTo(mouseX_Old, mouseY_Old);
                g.lineTo(mouseX, mouseY);
            }

            //発火元として描画
            {
                m_BitmapData_Fire.draw(fireShape);
                m_BitmapData_Fire.draw(fireShape_Fix);
            }

            //次回用更新
            {
                //揮発タイプは毎回リセット
                fireShape.graphics.clear();

                mouseX_Old = mouseX;
                mouseY_Old = mouseY;
            }
        }

        //Button
        public function OnMouseDown(e:MouseEvent):void{
            if(VIEW_H - 1*TEXT_OFFSET_Y < mouseY){
                Switch_UseSubtract();
                return;
            }
            if(VIEW_H - 2*TEXT_OFFSET_Y < mouseY){
                Switch_UseBlur();
                return;
            }
        }

        //DrawFire:炎の自動描画
        public function DrawFire():void{
            //描画処理
            {
                //ブラーで炎を広げる
                if(m_UseBlur)
                {
                    //薄めることで、上の方を細くする効果も兼ねる
                    m_BitmapData_Fire.applyFilter(m_BitmapData_Fire, VIEW_RECT, POS_ZERO, m_Filter_FireBlur);
                }

                //全体的に沈静化
                if(m_UseSubtract)
                {
                    //パーリンノイズで減衰することで揺らぎを表現
                    m_BitmapData_Fire.draw(m_BitmapData_PerlinNoise, m_Mtx_PerlinNoise, null, BlendMode.SUBTRACT);
                }
                else
                {
                    m_BitmapData_Fire.draw(m_BitmapData_PerlinNoise, m_Mtx_PerlinNoise, null, BlendMode.ADD);
                }

//                //そして0x00~0xFFの値を炎の色に置き換えて表示
//                if(m_UsePalette)
//                {
//                    m_BitmapData_View.paletteMap(m_BitmapData_Fire, VIEW_RECT, POS_ZERO, null,null,m_Palette_Fire_to_View);
//                }
            }

            //次回用の更新
            {
                const ScrollVal:int = 2;

                //炎を上にスクロールさせて、燃え上がりを実現
                {
                    //切り捨てて良いスクロールなので、普通にscrollを呼ぶ
                    m_BitmapData_Fire.scroll(0, -ScrollVal);
                }

                //パーリンノイズの採用位置を変更
                {
                    //横方向には少しだけ振動させ、上下方向は炎の上昇と合わせることで、それっぽい揺らぎを作り出す
                    m_Mtx_PerlinNoise.tx += (Math.random() < 0.5)? 1: -1;//振動
                    m_Mtx_PerlinNoise.ty -= ScrollVal;//追随
                    //範囲チェック
                    if(m_Mtx_PerlinNoise.tx > 0){m_Mtx_PerlinNoise.tx -= 2;}//範囲外は押し戻す
                    if(m_Mtx_PerlinNoise.tx < -VIEW_W){m_Mtx_PerlinNoise.tx += 2;}
                    if(m_Mtx_PerlinNoise.ty < -VIEW_H){m_Mtx_PerlinNoise.ty += VIEW_H;}//下にワープ
                }
            }
        }

        public function Switch_UseBlur():void{
            m_UseBlur = !m_UseBlur;

            Refresh_UseBlur();
        }
        public function Refresh_UseBlur():void{
            if(m_UseBlur){
                m_Text_UseBlur.text = "UseBlur : On";
            }else{
                m_Text_UseBlur.text = "UseBlur : Off";
            }
        }
/*
        public function Switch_UsePalette():void{
            m_UsePalette = !m_UsePalette;

            //!!描画colorやPaletteの内容も変更が必要
            if(m_UsePalette){
                m_Bitmap_Fire.parent.addChild(m_Bitmap_View);
                m_Bitmap_Fire.parent.removeChild(m_Bitmap_Fire);
            }else{
                m_Bitmap_View.parent.addChild(m_Bitmap_Fire);
                m_Bitmap_View.parent.removeChild(m_Bitmap_View);
            }
        }
//*/
        public function Switch_UseSubtract():void{
            m_UseSubtract = !m_UseSubtract;

            Refresh_UseSubtract();
        }
        public function Refresh_UseSubtract():void{
            if(m_UseSubtract){
                m_BitmapData_Fire.fillRect(m_BitmapData_Fire.rect, 0x000000);
                m_Text_UseSubtract.text = "UseSubtract : On";
            }else{
                m_BitmapData_Fire.fillRect(m_BitmapData_Fire.rect, 0xFFFFFF);
                m_Text_UseSubtract.text = "UseSubtract : Off";
            }

            fireShape_Fix.graphics.clear();
        }
    }
}