forked from: forked from: Saqoosha challenge for professionals(しんしん)

by jmbyh521 forked from forked from: Saqoosha challenge for professionals(しんしん) (diff: 1)
静かな降雪
♥0 | Line 345 | Modified 2015-12-30 15:59:54 | MIT License
play

ActionScript3 source code

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

// forked from Aquioux's forked from: Saqoosha challenge for professionals(しんしん)
// forked from checkmate's Saqoosha challenge for professionals
// 静かな降雪
package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Point;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    [SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
    
    public class Main2 extends Sprite {
        // 定数
        private const NUM_OF_FALL:uint = 5;    // 同時に落ちる粒子の数

        private const GRAVITY:Number        =  0.9;        // 重力
        private const BOUNCE:Number         = -0.0;        // 跳ね返り
        private const BEND:Number           =  0.0;        // 跳ね返るときに曲がる率
        private const FRICTION:Number       =  0.45;    // 空気抵抗
        private const FLOOR_FRICTION:Number =  0.0;        // 摩擦抵抗
        private const LOCAL_FRICTION_BOOLEAN:Boolean = true;
        private const BOTTOM:uint = stage.stageHeight - 1;
        
        private const LETTER:String = "しんしん";    // 表示文字列
        
        // パーティクルの色の設定
        private function setParticleColor():uint {
            var c:uint = Math.random() * 0x33 + 0xCC;
            return rgbToHex(c, c, c);
        }

        
        // 変数
        private var particleArray:Array;        // パーティクル格納配列(繰り返し使うために待避)
        private var copyParticleArray:Array;    // パーティクル格納配列(実際に使用する)
        
        private var canvasBitmapData:BitmapData;    // 描画 BitmapData
        private var pixelizer:Pixelizer;            // ピクセル化クラス

        
        // コンストラクタ
        public function Main2() {
            // Particle クラスの初期化
            Particle.gravity       = GRAVITY;
            Particle.bounce        = BOUNCE
            Particle.bend          = BEND;
            Particle.friction      = FRICTION;
            Particle.floorFriction = FLOOR_FRICTION;
            Particle.localFrictionBoolrean = LOCAL_FRICTION_BOOLEAN;
            Particle.top    = 0;
            Particle.bottom = BOTTOM;
            Particle.left   = 0;
            Particle.right  = stage.stageWidth;
            
            // 描画 BitmapData 生成
            canvasBitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0x000000);
            addChild(new Bitmap(canvasBitmapData));
            
            // 案内表示
            var signField:TextField   = new TextField();
            signField.text = "Stage を Click すると motion を再演します。";
            signField.autoSize   = TextFieldAutoSize.LEFT;
            signField.selectable = false;
            signField.blendMode  = BlendMode.INVERT;
            addChild(signField);

            // テキストピクセライズ
            var pTextField:PixelizerTextField = new PixelizerTextField();
            pTextField.text = LETTER;
            var textFormat:TextFormat = new TextFormat(null, 120);
            textFormat.letterSpacing = -5;
            pTextField.setTextFormat(textFormat);
            pixelizer = new Pixelizer();
            pixelizer.addEventListener(Event.COMPLETE, scanCompleteHandler);
            pixelizer.scan(pTextField.bitmapData);
        }
        
        // ピクセライズ完了後の処理
        // (ピクセル格納配列の生成)
        private function scanCompleteHandler(event:Event):void {
            // イテレータ生成
            var iter:PixelizerIterator = pixelizer.iterator;
            pixelizer = null;
            // 配置オフセット計算
            var offsetX:Number = (stage.stageWidth  - iter.width)  / 2;
            var offsetY:Number = 40;// (stage.stageHeight - iter.height) / 2;
            // イテレーション
            particleArray = [];
            while (iter.hasNext()) {
                var color:uint = iter.next();
                var alpha:uint = getAlpha(color);
                if (alpha > 0x7F) {
                    particleArray.push(new Particle(iter.x + offsetX, iter.y + offsetY, setParticleColor()));
                }
            }
            
            // イベント登録
            stage.addEventListener(MouseEvent.CLICK, clickHandler);
            motion();
        }
        
        // 実処理(モーションタイポ)
        private function motion():void {
            // 待避配列から使用配列を生成
            copyParticleArray = [];
            for each (var original:Particle in particleArray) {
                copyParticleArray.push(original.clone());
            }
            // Particle 格納配列をシャッフル
            shuffle(copyParticleArray);
            // 各 particle に動作開始時限値
            var n:uint = copyParticleArray.length;
            for (var i:int = 0; i < n; i++) {
                copyParticleArray[i].counter = i / NUM_OF_FALL;
            }
            
            // イベント登録
            addEventListener(Event.ENTER_FRAME, enterFrameHandler);
        }
        // フレームイベント
        private function enterFrameHandler(event:Event):void {
            canvasBitmapData.lock();
            canvasBitmapData.fillRect(canvasBitmapData.rect, 0x000000);
            for each (var particle:Particle in copyParticleArray) {
                particle.update();
                canvasBitmapData.setPixel(particle.x, particle.y, particle.color);
            }
            canvasBitmapData.unlock();
        }
        // マウスイベント
        private function clickHandler(event:MouseEvent):void {
            removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
            motion();
        }

        // シャッフル
        private function shuffle(a:Array):void {
            var n:int;
            var t:*;
            var l:uint = a.length;
            while (l--) {
                n = Math.floor(Math.random() * (l+1));
                t = a[l];
                a[l] = a[n];
                a[n] = t;
            }
        }
        // RGB → 0xNNNNNN
        private function rgbToHex(r:uint, g:uint, b:uint):uint {
            r = adjust(r);
            g = adjust(g);
            b = adjust(b);

            return r << 16 | g << 8 | b;
        }
        // 255 を超えていた場合は 255 に切り捨てる
        private function adjust(val:uint):uint {
            return Math.min(val, 0xFF);
        }

        // 32 bit color のアルファ値を取得
        private function getAlpha(color:uint):uint {
            return (color >> 24) & 0xFF;
        }
        
    }
    
}

// パーティクルクラス
    class Particle {
        // static 変数
        // 物理変数
        public static var gravity:Number       =  0.98;    // 重力
        public static var bounce:Number        = -0.45;    // 跳ね返り
        public static var bend:Number          =  4.5;    // 跳ね返るときに曲がる率
        public static var friction:Number      =  0.98;    // 空気抵抗
        public static var floorFriction:Number =  0.9;    // 摩擦抵抗
        
        public static var localFrictionBoolrean:Boolean = false;
        
        // ステージ領域
        public static var top:uint;
        public static var bottom:uint;
        public static var left:uint;
        public static var right:uint;


        // 現在座標
        public function get x():Number { return _x; }
        private var _x:Number;
        public function get y():Number { return _y; }
        private var _y:Number;
        // 色
        public function get color():uint { return _color; }
        private var _color:uint;
        
        // 動作開始時限値
        public function set counter(value:uint):void {
            _counter = value;
        }
        private var _counter:uint = 0;
        

        // ローカルな物理変数
        private var localBounce:Number;        // 跳ね返り
        private var localBend:Number;        // 跳ね返るときに曲がる率
        private var localFriction:Number;    // 空気抵抗
        
        // 速度
        private var vx:Number = 0;
        private var vy:Number = 0;


        public function Particle(x:Number, y:Number, color:uint) {
            _x = x;
            _y = y;
            _color = color;
            
            // bounce、bend、friction のローカル補正
            localBounce = bounce + (Math.random() * 2 - 1) / 4;
            localBend = (Math.random() < 0.5) ? bend : -bend;
            localBend += (Math.random() * 2 - 1);
            localFriction = (localFrictionBoolrean) ? friction - Math.random() : friction - Math.random() / 20;
        }
        
        public function clone():Particle {
            return new Particle(_x, _y, _color);
        }
        
        public function update():void {
            if (_counter > 0) {
                _counter--;
            } else {
                // 壁処理
                if (_x > right) {
                    _x = right;
                    vx *= localBounce;
                    vx *= floorFriction;
                    localBend *= floorFriction;
                    vy = localBend;
                } else if (_x < left) {
                    _x = left;
                    vx *= localBounce;
                    vx *= floorFriction;
                    localBend *= floorFriction;
                    vy = localBend;
                }
                if (_y > bottom) {
                    _y = bottom;
                    vy *= localBounce;
                    vy *= floorFriction;
                    localBend *= floorFriction;
                    vx = localBend;
                } else if (_y < top) {
                    _y = top;
                    vy *= localBounce;
                    vy *= floorFriction;
                    localBend *= floorFriction;
                    vx = localBend;
                }
                // 座標更新
                vx *= localFriction;
                vy *= localFriction;
                vy += gravity;
                _x += vx;
                _y += vy;
            }
        }
    }

// ピクセル化クラス
    import flash.display.BitmapData;
    import flash.events.Event;
    import flash.events.EventDispatcher;

    class Pixelizer extends EventDispatcher {
        private var width:uint  = 0;
        private var height:uint = 0;
        private var data:Vector.<uint>;
        
        // イテレータ
        public function get iterator():PixelizerIterator {
            return new PixelizerIterator(width, height, data);
        }
        
        // コンストラクタ
        public function Pixelizer() {}

        // スキャン
        public function scan(bmd:BitmapData):void {
            width  = bmd.width;
            height = bmd.height;
            data   = bmd.getVector(bmd.rect);

            // イベント発行
            dispatchEvent(new Event(Event.COMPLETE));
        }
    }

// ピクセル化したデータのイテレータ
    class PixelizerIterator {
        // モードフラグ
        public static const NEXT:String = "next";    // 正順
        public static const PREV:String = "prev";    // 逆順

        // next()、nextCol() で取得したデータのX座標
        public function get x():int { return positionX; }
        private var positionX:int = 0;
        // next()、nextRow() で取得したデータのY座標
        public function get y():int { return positionY; }
        private var positionY:int = 0;

        // スキャンサイズ(幅)
        public function get width():uint { return _width; }
        private var _width:uint;
        // スキャンサイズ(高)
        public function get height():uint { return _height; }
        private var _height:uint;

        // データ格納 Vector
        private var data:Vector.<uint>;

        // 現在のイテレートモード
        private var mode:String = "next";
        // イテレーションカウンター
        private var position:int = 0;
        
        
        public function PixelizerIterator(width:uint, height:uint, data:Vector.<uint>) {
            _width  = width;
            _height = height;
            this.data = data;
        }
        
        // データの操作(1個ずつデータを呼び出す)
        // 外部から呼び出せる hasNext
        public function hasNext():Boolean {
            return (mode == "prev") ? _hasPrev() : _hasNext();
        }
        // 外部から呼び出せる next
        public function next():uint {
            return (mode == "prev") ? _prev() : _next();
        }

        // データの同一列(横)の操作(同一列の全てのデータを呼び出す)
        // 外部から呼び出せる hasNext
        public function hasNextRow():Boolean {
            return (mode == "prev") ? _hasPrevRow() : _hasNextRow();
        }
        // 外部から呼び出せる next
        public function nextRow():Vector.<uint> {
            return (mode == "prev") ? _prevRow() : _nextRow();
        }

        // データの同一行(縦)の操作(同一行のすべてのデータを呼び出す)
        // 外部から呼び出せる hasNext
        public function hasNextCol():Boolean {
            return (mode == "next") ? _hasNextCol() : _hasPrevCol();
        }
        // 外部から呼び出せる next
        public function nextCol():Vector.<uint> {
            return (mode == "next") ? _nextCol() : _prevCol();
        }

        // リセット
        public function reset(mode:String = "next"):void {
            this.mode = mode;
            var offset:uint = (mode == "next") ? 0 : 1;
            position  = (data.length - 1) * offset;
            positionX = (_width  - 1) * offset;
            positionY = (_height - 1) * offset;
        }


        // データの操作(1個ずつデータを呼び出す)
        // mode = "next" 時の hasNext
        private function _hasNext():Boolean {
            return position < data.length;
        }
        // mode = "prev" 時の hasNext
        private function _hasPrev():Boolean {
            return position > 0;
        }
        // mode = "next" 時の next
        private function _next():uint {
            positionX = position % _width;
            positionY = position / _width;
            return getData(position++);
        }
        // mode = "prev" 時の next
        private function _prev():uint {
            positionX = position % _width;
            positionY = position / _width;
            return getData(position--);
        }
        private function getData(idx:uint):uint {
            if (idx >= data.length) {
                throw new Error("getData#配列範囲外:" + idx + " length:" + data.length);
            }
            return data[idx];
        }
        
        // データの同一列(横)の操作(同一列の全てのデータを呼び出す)
        // mode = "next" 時の hasNext
        private function _hasNextRow():Boolean {
            return positionY < _height;
        }
        // mode = "prev" 時の hasNext
        private function _hasPrevRow():Boolean {
            return positionY > -1;
        }
        // mode = "next" 時の next
        private function _nextRow():Vector.<uint> {
            return getRow(positionY++);
        }
        // mode = "prev" 時の next
        private function _prevRow():Vector.<uint> {
            return getRow(positionY--);
        }
        // next の実体
        private function getRow(val:uint):Vector.<uint> {
            if (val >= _height) {
                throw new Error("getRow#配列範囲外:" + val + " _height:" + _height);
            }
            return data.slice(val * _width, (val + 1) * _width);
        }
        
        // データの同一行(縦)の操作(同一行のすべてのデータを呼び出す)
        // mode = "next" 時の hasNext
        private function _hasNextCol():Boolean {
            return positionX < _width;
        }
        // mode = "prev" 時の hasNext
        private function _hasPrevCol():Boolean {
            return positionX > -1;
        }
        // mode = "next" 時の next
        private function _nextCol():Vector.<uint> {
            return getCol(positionX++);
        }
        // mode = "prev" 時の next
        private function _prevCol():Vector.<uint> {
            return getCol(positionX--);
        }
        // next の実体
        private function getCol(idx:uint):Vector.<uint> {
            if (idx >= _width) {
                throw new Error("getCol#配列範囲外:" + idx + " _width:" + _width);
            }
            var vector:Vector.<uint> = new Vector.<uint>(_height, true);
            for (var i:uint = 0; i < _height; i++) {
                vector[i] = data[_width * i + idx];
            }
            return vector;
        }
    }

// 文字列をピクセル化クラスへ投げ込むためのテキストフィールド
    import flash.display.BitmapData;
    import flash.geom.Matrix;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;

    class PixelizerTextField extends TextField {
        // 透明
        private const TRANSPARENT:uint = 0x00000000;


        public function PixelizerTextField() {
            autoSize = TextFieldAutoSize.LEFT;
        }

        public function get bitmapData():BitmapData {
            // 普通に TextField を BitmapData に draw
            var bmd1:BitmapData = new BitmapData(textWidth, textHeight, true, TRANSPARENT);
            bmd1.draw(this, new Matrix(1, 0, 0, 1, -2, -2));
            // 上記 BitmapData のうち、文字である範囲を Rectangle として取得
            var rect:Rectangle = bmd1.getColorBoundsRect(0xFF000000, TRANSPARENT, false);
            // 上記 Rectangle 部分のみの BitmapData を生成
            var bmd2:BitmapData = new BitmapData(rect.width, rect.height, true, TRANSPARENT);
            bmd2.draw(bmd1, new Matrix(1, 0, 0, 1, -rect.x, -rect.y));
            bmd1.dispose();

            return bmd2;
        }
    }