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

反応拡散もどき(ライフゲーム拡張)

反応拡散を真面目に計算するとパラメータ調整が難しかったので、ライフゲームを拡張してそれっぽい挙動にしてみた。

リロードのたびに模様は変わります。
マウスで消すこともできます。(Shiftを押しながらだと広範囲を消します)
Get Adobe Flash player
by o_healer 25 Sep 2010
/**
 * 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/wPAT
 */

/*
 「反応拡散もどき」

 概要
 ・ライフゲームの応用で、反応拡散っぽい動きをするものを作ってみた

 動作説明
 ・毎回ちょっと違うパターンが生成される
 ・マウスで色を消すことが可能。Shiftを押しながらだろさらに広範囲を消せる。

 解説
 ・ベタに反応拡散系をいじろうとすると、パラメータの調整が難しかったので、
  「生存(死亡)条件」がわかりやすいライフゲームを拡張してそれっぽいものを作ってみた
 ・ConvolutionFilterでライフゲームもどきを実行しつつ、BlurFilterで拡散させているだけ
 ・とりあえず「上下に移動するか斜めに移動するか(Alpha)」と「反応のしやすさ(Beta)」をなんとなくパラメータ化できた
 
*/

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 GameMain extends Sprite {

        //==Const==

        //サイズ
        static public const SCL:int = 2;//4;//大きくすると荒くなる(高速化される)
        static public const BMD_W:int = 465/SCL;//100;

        //速度
        static public const SPEED:int = 1;//試行回数(何倍速で実行するか)

        //Utility
        static public const BMD_RECT:Rectangle = new Rectangle(0,0,BMD_W,BMD_W);
        static public const POS_ZERO:Point = new Point(0,0);

        //Debug
        static public const DEBUG:Boolean = false;

//*
        //拡散反応もどき:乱数バージョン
        //Alphaが大きいと離れた位置に自然発生が起こり、小さいと四角い移動パターンになる
        //Betaが大きいと広がりやすく、小さいと広がりにくい
        //再現のため、乱数に桁数制限を入れてみる
        static public const Alpha:Number = 2.1 - 4.2*int(Math.random()*100)/100;//2.1~-2.1:実際には2.1~-5くらいまでOK
        static public const Beta:Number  = 0.3 - 0.6*int(Math.random()*100)/100;//0.3~-0.3
        //閾値
        static public const A:Number = 5.82 + Beta;
        //上下
        static public const B:Number = 2 + Alpha;
        static public const D:Number = -0.1 - Alpha;
        //斜め
        static public const C:Number = 0.2;
        static public const E:Number = -1.2 - 0.5*Alpha;
        static public const F:Number = -1 + Alpha;

        static public const Blur:Number = 4.5;

        //左下にデバッグ表示
        static public const DEBUG_NUMBER_LEN:int = 3;//桁数表示制限
        static public const DEBUG_STR:String =
            "A:" + A.toFixed(DEBUG_NUMBER_LEN) + "  " +
            "B:" + B.toFixed(DEBUG_NUMBER_LEN) + "  " +
            "C:" + C.toFixed(DEBUG_NUMBER_LEN) + "  " +
            "D:" + D.toFixed(DEBUG_NUMBER_LEN) + "  " +
            "E:" + E.toFixed(DEBUG_NUMBER_LEN) + "  " +
            "F:" + F.toFixed(DEBUG_NUMBER_LEN);
/*/
        static public const DEBUG_STR:String = "";
//*/

        //==Var==

        //表示用
        public var m_BitmapData_View:BitmapData;
        public var PALLETE_MAP:Array;

        //Debug
        public var m_BitmapData_View_U:BitmapData;
        public var m_BitmapData_View_V:BitmapData;

        //拡散反応用
        public var m_BitmapData_Src_U:BitmapData;

        //テキスト
        public var m_Text:TextField = new TextField();

        //==Function==

        //Init
        public function GameMain():void {
            //表示用
            {
                //画像
                {
                    m_BitmapData_View = new BitmapData(BMD_W, BMD_W, false, 0x000000);
                    var bmp_view:Bitmap = new Bitmap(m_BitmapData_View);
                    bmp_view.scaleX = SCL;
                    bmp_view.scaleY = SCL;
                    addChild(bmp_view);
                }

                //Palette
                {
                    const COLOR_00:uint = 0x111111;
                    const COLOR_FF:uint = 0xCCCC33;

                    const COLOR_00_R:uint = (COLOR_00 >> 16) & 0xFF;
                    const COLOR_00_G:uint = (COLOR_00 >>  8) & 0xFF;
                    const COLOR_00_B:uint = (COLOR_00 >>  0) & 0xFF;
                    const COLOR_FF_R:uint = (COLOR_FF >> 16) & 0xFF;
                    const COLOR_FF_G:uint = (COLOR_FF >>  8) & 0xFF;
                    const COLOR_FF_B:uint = (COLOR_FF >>  0) & 0xFF;

                    const Lerp:Function = function(in_Src:uint, in_Dst:uint, in_Ratio:Number):uint{
                        return (in_Src * (1-in_Ratio)) + (in_Dst * in_Ratio);
                    };

                    PALLETE_MAP = new Array(256);
                    for(var i:int = 0; i < 256; i++){
                        var ratio:Number = i / (255.0);
                        ratio = 0.5 - 0.5*Math.cos(Math.PI*ratio);//色をややくっきり分けてみる
                        ratio = 0.5 - 0.5*Math.cos(Math.PI*ratio);//さらにくっきり
                        ratio = 0.5 - 0.5*Math.cos(Math.PI*ratio);//さらにくっきり

                        var r:uint = Lerp(COLOR_00_R, COLOR_FF_R, ratio);
                        var g:uint = Lerp(COLOR_00_G, COLOR_FF_G, ratio);
                        var b:uint = Lerp(COLOR_00_B, COLOR_FF_B, ratio);

                        PALLETE_MAP[i] = (r << 16) | (g << 8) | (b << 0);
                    }
                }
            }

            //Debug
            if(DEBUG)
            {
                var bmp:Bitmap;

                m_BitmapData_View_U = new BitmapData(BMD_W, BMD_W, false, 0x000000);
                bmp = new Bitmap(m_BitmapData_View_U);
                addChild(bmp);

                m_BitmapData_View_V = new BitmapData(BMD_W, BMD_W, false, 0x000000);
                bmp = new Bitmap(m_BitmapData_View_V);
                bmp.x = BMD_W;
                addChild(bmp);
            }

            //拡散反応用
            {
                //生成
                {
                    m_BitmapData_Src_U = new BitmapData(BMD_W, BMD_W, false, 0x000000);
                }

                //初期値設定
                {
/*
                    //パーリンノイズ
                    m_BitmapData_Src_U.perlinNoise(BMD_W, BMD_W, 2, Math.random()*100, false, true, BitmapDataChannel.BLUE);
//*/
/*
                    //完全ランダム
                    m_BitmapData_Src_U.noise(Math.random()*100, 0x00,0xFF, BitmapDataChannel.BLUE);
//*/
//*
                    //中央に四角
                    m_BitmapData_Src_U.fillRect(new Rectangle(BMD_W*3/8, BMD_W*3/8, BMD_W/4, BMD_W/4), 0x0000FF);
//*/
                }
            }

            //Mouse
            {
                var shape:Shape = new Shape();
                var gr:Graphics = shape.graphics;

                var Draw:Function = function(in_SrcX:int, in_SrcY:int, in_DstX:int, in_DstY:int, in_IsShift:Boolean):void{
                    gr.clear();

                    var DOT_W:int = in_IsShift? BMD_W/4: BMD_W/16;
                    var color:uint = 0x000000;

                    if(Math.abs(in_SrcX - in_DstX) < 1 && Math.abs(in_SrcY - in_DstY) < 1){
                        //点
                        gr.lineStyle(0,0,0);
                        gr.beginFill(color, 1.0);
                        gr.drawCircle(in_SrcX, in_SrcY, DOT_W/2);
                        gr.endFill();
                    }else{
                        //線
                        gr.lineStyle(DOT_W, color, 1.0);

                        gr.moveTo(in_SrcX, in_SrcY);
                        gr.lineTo(in_DstX, in_DstY);
                    }

                    m_BitmapData_Src_U.draw(shape);
                };

                addEventListener(
                    Event.ADDED_TO_STAGE,
                    function(e:Event):void{
                        var IsMouseDown:Boolean = false;

                        var oldMouseX:int = bmp_view.mouseX;
                        var oldMouseY:int = bmp_view.mouseY;

                        stage.addEventListener(
                            MouseEvent.MOUSE_DOWN,
                            function(event:MouseEvent):void{
                                IsMouseDown = true;

                                Draw(oldMouseX, oldMouseY, bmp_view.mouseX, bmp_view.mouseY, event.shiftKey);
                            }
                        );
                        stage.addEventListener(
                            MouseEvent.MOUSE_MOVE,
                            function(event:MouseEvent):void{
                                if(IsMouseDown){
                                    Draw(oldMouseX, oldMouseY, bmp_view.mouseX, bmp_view.mouseY, event.shiftKey);
                                }
                                oldMouseX = bmp_view.mouseX;
                                oldMouseY = bmp_view.mouseY;
                            }
                        );
                        stage.addEventListener(
                            MouseEvent.MOUSE_UP,
                            function(event:MouseEvent):void{
                                IsMouseDown = false;
                            }
                        );
                    }
                );
            }

            //Text
            {
                m_Text.selectable = false;
                m_Text.autoSize = TextFieldAutoSize.LEFT;
                m_Text.defaultTextFormat = new TextFormat('Verdana', 12, 0x00FFFF, true);
                m_Text.text = DEBUG_STR;
                m_Text.filters = [new GlowFilter(0x000000, 1.0, 4,4, 255)];
                m_Text.x = 0;
                m_Text.y = 465 - 24;

                addChild(m_Text);
            }

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

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

            //拡散反応処理
            for(var i:int = 0; i < SPEED; i++){
                Update_ReactionDiffusion();
            }

            //結果を可視化
            Redraw();
        }

        //Update:拡散反応(もどき)
        public function Update_ReactionDiffusion():void{
            //ライフゲーム相当

            //基本的な考え方
            //・隣接方向には拡大しようとする(中心に近い部分はプラス)
            //・まわりを全て囲まれたら消えようとする(配列の値を全て合計したらマイナス)
/*
            //ノーマル(曲がろうとする直線メイン、分離あり)
            const A:Number = 5;
            const B:Number = 1;
            const C:Number = 0.5;
            const D:Number = -1;
            const E:Number = -0.8;
            const F:Number = 0;

            const Blur:Number = 5.0;
//*/

/*
            //やや分離ありの直角メイン
            const A:Number = 5.82;
            const B:Number = 1;
            const C:Number = 0.2;
            const D:Number = -1;
            const E:Number = -0.71;
            const F:Number = 0;

            const Blur:Number = 5.0;
//*/

/*
            //分離あり
            const A:Number = 5.82;
            const B:Number = 1.1;
            const C:Number = 0.1;
            const D:Number = -0.88;
            const E:Number = -0.80;
            const F:Number = 0;

            const Blur:Number = 5.0;
//*/

/*
            //分離・合体
            const A:Number = 5.82;
            const B:Number = 2.2;
            const C:Number = 0.2;
            const D:Number = -0.1;
            const E:Number = -1.2;
            const F:Number = -1.25;

            const Blur:Number = 4.5;
//*/

/*
            //分離・合体2
            const A:Number = 5.82;
            const B:Number = 1.692;
            const C:Number = 0.2;
            const D:Number = 0.208;
            const E:Number = 1.046;
            const F:Number = -1.303;

            const Blur:Number = 4.5;
//*/

/*
            //遠隔発生
            const A:Number = 5.82;
            const B:Number = 4.0;
            const C:Number = 0.2;
            const D:Number = -2.1;
            const E:Number = -2.2;
            const F:Number = 1.0;

            const Blur:Number = 4.5;
//*/

/*
            //四角
            const A:Number = 5.82;
            const B:Number = -2.0;
            const C:Number = 0.2;
            const D:Number = 3.9;
            const E:Number = 0.8;
            const F:Number = -5.0;

            const Blur:Number = 4.5;
//*/

            const filter_conv:ConvolutionFilter = new ConvolutionFilter(
                5,5,
                [
                    F, E, D, E, F,
                    E, C, B, C, E,
                    D, B, A, B, D,
                    E, C, B, C, E,
                    F, E, D, E, F,
                ]
            );
            m_BitmapData_Src_U.applyFilter(m_BitmapData_Src_U, BMD_RECT, POS_ZERO, filter_conv);

            //拡散
            const filter_blur:BlurFilter = new BlurFilter(Blur, Blur);
            m_BitmapData_Src_U.applyFilter(m_BitmapData_Src_U, BMD_RECT, POS_ZERO, filter_blur);
        }

        //Redraw
        public function Redraw():void{
            //可視化
            {
                //m_BitmapData_Src_Uの可視化(Src側に今回の結果がフィードバックされているものとする)
                m_BitmapData_View.paletteMap(m_BitmapData_Src_U, BMD_RECT, POS_ZERO, null, null, PALLETE_MAP);
            }

            //Debug
            if(DEBUG){
                m_BitmapData_View_U.copyPixels(m_BitmapData_Src_U, BMD_RECT, POS_ZERO);
            }
        }
    }
}