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

Flock simuration

群れ(flock)のシミュレーション
Get Adobe Flash player
by flashrod 21 Jan 2009
/**
 * Copyright flashrod ( http://wonderfl.net/user/flashrod )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/b3BQ
 */

// 群れ(flock)のシミュレーション
package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Matrix;
    import flash.geom.Point;

    /** 群れのシミュレーション */
    [SWF(width="465", height="465", backgroundColor="0x00FFFF", frameRate="30")]
    public class Flock extends Sprite {
        public static const W:int = 465;
        public static const H:int = 465;
        public static const N:int = 8*2;

        /** Array of Individual */
        private var crowd:Array = [];

        private var m2v:Matrix;
        private var v2m:Matrix;

        public function Flock() {
            m2v = new Matrix();
            m2v.scale(4, 4);
            m2v.translate(W/2, H/2);
            v2m = m2v.clone();
            v2m.invert();
            for (var i:int = 0; i < N; ++i) {
                crowd.push(new Individual(new Point(Math.random() * 10.0 - 5.0, Math.random() * 10.0 - 5.0),
                                          Math.random() * 3 + 3,
                                          Math.random() * Math.PI / 2.0));
            }
            addEventListener(Event.ENTER_FRAME, step);
        }

        /**
         * @param ev enterFrameイベント
         */
        public function step(ev:Event):void {
            var esc:Point = v2m.transformPoint(new Point(mouseX, mouseY)); // マウスを避ける
            var c:Point = center();       // 重心
            var a:Array = crowd.concat(); // 複製を作る
            while (a.length > 0) {
                var i1:Individual = a.shift(); // 取り出す
                var i2:Individual = nearest(a, i1.position);
                i1.step(c, i2, esc);
                i2.step(c, i1, esc);
                a.splice(a.indexOf(i2), 1); // 取り除く
            }
            repaint();
        }

        /** 重心を得る
         * @return 重心
         */
        private function center():Point {
            var p:Point = new Point();
            for each (var b:Individual in crowd) {
                p = p.add(b.position);
            }
            p.x /= N;
            p.y /= N;
            return p;
        }

        /** 一番近いやつを得る
         * @param a 個体集合
         * @param p 検査点 
         * @return pに一番近い個体(無いときはnull)
         */
        private static function nearest(a:Array, p:Point):Individual {
            var i:Individual = null;
            var min:Number = Number.MAX_VALUE;
            for each (var j:Individual in a) {
                var d:Number = j.position.subtract(p).length;
                if (d < min) {
                    min = d;
                    i = j;
                }
            }
            return i;
        }

        /** 再描画
         */
        private function repaint():void {
            graphics.clear();
            for each (var i:Individual in crowd) {
                var p:Point = m2v.transformPoint(i.position);
                FishPainter.paint(graphics, p, i.orientation, i.state);
            }
        }
    }
}

import flash.geom.Point;
/** 個体 */
class Individual {
    /** 最適距離 */
    public static const SUITABLE:Number = 10.0;
    /** 加速度 */
    public static const ACCELERATION:Number = 1.05;
    /** 位置 */
    public var position:Point;
    /** 向き(単位ベクトル) */
    public var orientation:Point;
    /** 速度 */
    public var speed:Number;

    /** 状態 */
    public var state:int;
    /** 状態を進めるカウント */
    private var count:int;

    /** 個体の構築
     * @param p 位置
     * @param s 速度
     * @param t 向きを表す角度(radian)
     */
    public function Individual(p:Point, s:Number, t:Number) {
        this.position = p;
        this.orientation = new Point(Math.cos(t), Math.sin(t));
        this.speed = s;
    }

    /**
     * @param c 群れの重心
     * @param i 一番近い個体
     * @param esc 避ける点
     */
    public function step(c:Point, i:Individual, esc:Point):void {
        // 避ける点に近いときは逃げる
        if (Point.distance(position, esc) < SUITABLE) {
            orientation = position.subtract(esc);
            orientation.normalize(1.0);
            speed = 10;
        }
        // この個体を原点とする一番近い個体の位置
        var v:Point = i.position.subtract(position);
        // 一番近い個体との距離
        var d:Number = v.length;
        // 一番近くの個体が前にいるか後ろにいるか調べて、一番近くの個体に速度を合わせる
        v = v.subtract(position);
        v = rotate(v, orientation.y, -orientation.x);
        v = v.add(position);
        if (v.x < 0) { // 最も近い個体が前にいる
            if (d < SUITABLE) { // 最適距離よりも近い
                speed /= ACCELERATION; // 速度を落とす
            } else if (d > SUITABLE) { // 最適距離よりも遠い
                speed *= ACCELERATION; // 追い上げる
            }
        } else if (v.x > 0) { // 最も近い個体が後ろにいる
            if (d < SUITABLE) { // 最適距離よりも近い
                speed *= ACCELERATION; // 逃げる
            } else if (d > SUITABLE) { // 最適距離よりも遠い
                speed /= ACCELERATION; // 待ち受ける
            }
        }
        speed = Math.min(Math.max(3, speed), 20);
        // その個体と平行に進もうとする
        orientation.offset(i.orientation.x * 0.1, i.orientation.y * 0.1);
        // 重心に向かわせる
        orientation.offset((c.x - position.x) * 0.01, (c.y - position.y) * 0.01);
        // (0,0)に向かわせる
        orientation.offset(-position.x * 0.001, -position.y * 0.001);
        // orientationの単位ベクトル化
        orientation.normalize(1.0);
        // 移動分Δ
        var delta:Point = new Point(orientation.x * speed / 20, orientation.y * speed / 20);
        // 個体の位置を更新
        position = position.add(delta);
        // 範囲に抑え込む
        position.x = Math.min(Math.max(-100, position.x), 100);
        position.y = Math.min(Math.max(-100, position.y), 100);
        // 状態を進める
        if (--count < 0) {
            state ^= 1;
            count = 10/speed;
        }
    }

    /** (0,0)を中心にpをθ回転
     * @param p 回転前の点
     * @param sint sin(θ)
     * @param cost cos(θ)
     * @return 回転後の新しい点
     */
    public static function rotate(p:Point, sint:Number, cost:Number):Point {
        return new Point(p.x * cost - p.y * sint, p.x * sint + p.y * cost);
    }
}

import flash.display.Graphics;
import flash.geom.Point;
import flash.geom.Matrix;
// 魚の描画データは以下からもらってきた
// うお
// http://wonderfl.kayac.com/code/1f31037492a64576ea9137cb9046359b6173cde6
class FishPainter {

    private static const d1:Array =
        [[new Point(1.4, -2.1), new Point(8, -2.5)],
         [new Point(10, -4.5), new Point(11, -4.5)],
         [new Point(11, -2.5), new Point(10, -2.5)],
         [new Point(14, -2.5), new Point(22, -1.5)],
         [new Point(26.6, -3), new Point(27, -2)],
         [new Point(26.6, -1), new Point(26, -1)],
         [new Point(27.6, -1), new Point(28.8, 1.2)],
         [new Point(27.6, 2.5), new Point(22, -.5)],
         [new Point(14, 2.5), new Point(10, 2.5)],
         [new Point(11, 2.5), new Point(11, 4.5)],
         [new Point(10, 4.5), new Point(8, 2.5)],
         [new Point(1.4, 2.1), new Point(0, 0)]];

    private static const d2:Array =
        [[new Point(1.4, -2.1), new Point(8, -2.5)],
         [new Point(10, -3.5), new Point(11, -3.5)],
         [new Point(11, -2.5), new Point(10, -2.5)],
         [new Point(14, -2.5), new Point(22, .5)],
         [new Point(27.6, -2), new Point(28.8, -1)],
         [new Point(27.6, 1), new Point(26, 1)],
         [new Point(26.6, 2), new Point(27, 2.2)],
         [new Point(26.6, 3.5), new Point(22, 1.5)],
         [new Point(14, 2.5), new Point(10, 2.5)],
         [new Point(11, 2.5), new Point(11, 3.5)],
         [new Point(10, 3.5), new Point(8, 2.5)],
         [new Point(1.4, 2.1), new Point(0, 0)]];

    /** 一個体を描く
     * @param g 描画先
     * @param p 描画位置
     * @param o 向き(方向ベクトル)
     * @param s 状態(0か1)
     */
    public static function paint(graphics:Graphics, p:Point, o:Point, s:int):void {
        var x:Number = p.x;
        var y:Number = p.y;
        var m:Matrix = new Matrix();
        m.rotate(Math.atan2(o.y, o.x) + Math.PI);
        var data:Array = s == 0 ? d1 : d2;
        graphics.beginFill(0xFF0000);
        graphics.moveTo(x, y);
        for each (var d:Array in data) {
            var c1:Point = m.transformPoint(d[0]);
            var c2:Point = m.transformPoint(d[1]);
            graphics.curveTo(x+c1.x, y+c1.y, x+c2.x, y+c2.y);
        }
        graphics.endFill();
    }
}