Flock simuration
群れ(flock)のシミュレーション
/**
* 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();
}
}