鳥の群れみたいな何か
/**
* Copyright keno42 ( http://wonderfl.net/user/keno42 )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/veAl
*/
package
{
import flash.display.Sprite;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Point;
[SWF(framerate=60, backgroundColor="#000000")]
public class Boids extends Sprite
{
private const COUNT:int = 70; // 個体数
private const CHANGE_TARGET_RATE:Number = 0.02; // えさ場(?)変更の割合
private var firstBird:Bird = new Bird();
private var center:Point = new Point();
private var bg:Sprite = new Sprite(); // グリッド描画
private var birds:Sprite = new Sprite(); // みなさんの置き場
private var targetPoint:Point = new Point(); // えさ場(?)の場所
public function Boids()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
birds.x = 232;
birds.y = 232;
addChild( bg );
addChild( birds );
// 設置
birds.addChild(firstBird);
var lastBird:Bird = firstBird;
for (var i:int = 1; i < COUNT; i++ ) {
var bird:Bird = new Bird();
lastBird.next = bird;
lastBird = bird;
birds.addChild(bird);
}
// 背景は黒くしとく
this.graphics.beginFill(0);
this.graphics.drawRect(0,0,465,465);
// 背景グリッド(無駄に大きめ)
bg.graphics.clear();
bg.graphics.lineStyle(0, 0x00FF00, 0.25);
for ( var i:int = 0; i < 20; i++ ) {
bg.graphics.moveTo(60*i, 0);
bg.graphics.lineTo(60*i, 1000);
bg.graphics.moveTo(0, 60*i);
bg.graphics.lineTo(1000, 60*i);
}
changeTarget();
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
/**
* 毎フレーム更新
*/
private function onEnterFrame(e:Event):void {
// えさ場(?)の変更
if ( Math.random() < 0.01 ) changeTarget();
// 中心位置とか計算用の初期化
center.x = 0;
center.y = 0;
var maxX:Number = -99999999999999999999;
var minX:Number = 99999999999999999999;
var maxY:Number = -99999999999999999999;
var minY:Number = 99999999999999999999;
// 工夫ゼロの2重ループなのでめちゃ重い。O(N^2)
var currentBird:Bird = firstBird;
do {
var targetBird:Bird = firstBird;
var nearestD:Number = 9999999999999999999999;
do {
if ( targetBird == currentBird ) continue;
var targetX:Number = targetBird.x - currentBird.x;
var targetY:Number = targetBird.y - currentBird.y;
var distance:Number = targetX * targetX + targetY * targetY;
if ( distance < nearestD ) {
currentBird.nearestBird = targetBird;
nearestD = distance;
}
} while ( targetBird = targetBird.next )
currentBird.nearestD = nearestD;
center.x += currentBird.x;
center.y += currentBird.y;
if ( currentBird.x > maxX ) maxX = currentBird.x;
if ( currentBird.x < minX ) minX = currentBird.x;
if ( currentBird.y > maxY ) maxY = currentBird.y;
if ( currentBird.y < minY ) minY = currentBird.y;
} while ( currentBird = currentBird.next )
// 座標の加重平均
center.x /= COUNT;
center.y /= COUNT;
// 各自が行動を決定
currentBird = firstBird;
do {
currentBird.centerX = targetPoint.x;
currentBird.centerY = targetPoint.y;
currentBird.update();
// currentBird.nearestBird = null;
// currentBird.nearestD = 0;
} while ( currentBird = currentBird.next )
// カメラのスケール
// var scale:Number = Math.min( 465 / (maxX - minX + 20), 465 / (maxY - minY + 20), 1 );
var scale:Number = 1;
birds.scaleX += 0.1 * (scale-scaleX);
birds.scaleY += 0.1 * (scale-scaleY);
// カメラ中心の移動
birds.x += 0.1 * (232 - center.x - birds.x);
birds.y += 0.1 * (232 - center.y - birds.y);
// 背景グリッド描画
drawbg();
}
private function changeTarget():void {
targetPoint.x = Math.random() * 1500 - 750;
targetPoint.y = Math.random() * 1500 - 750;
}
private function drawbg():void {
var gridStartX:Number = (birds.x) % 60 - 300;
var gridStartY:Number = (birds.y) % 60 - 300;
bg.x = gridStartX;
bg.y = gridStartY;
}
}
}
import flash.display.MovieClip;
class Bird extends MovieClip
{
private const MAX_V:Number = 5; // 最大速度
private const MIN_V:Number = 1; // 最低速度
private const AVE_V:Number = 0.5 * (MAX_V - MIN_V) + MIN_V;
private const V_RATE:Number = 0.1; // 速度変更時の掛け率
private const A_RATE:Number = 0.15; // 角度変更時の掛け率
private const FOLLOW_RATE:Number = 0.1; // 一番近いのに素直についていく確率
private const AVOID_ANGLE:Number = 0.5; // どのくらいあわてて避けるか?
private var myColor:uint = Math.random() * 0xFFFFFF | 0x444444;
public var angle:Number = 2 * Math.PI * Math.random();
public var velocity:Number = Math.random() * (MAX_V - MIN_V) + MIN_V;
public var nearestD:Number;
public var nearestBird:Bird;
public var centerX:Number;
public var centerY:Number;
public var next:Bird;
private const AVOID_DISTANCE:Number = Math.pow( 20 + 5 * Math.random(), 2); // 避ける距離
private const FOLLOW_DISTANCE:Number = Math.pow( 30 + 20 * Math.random(), 2); // ついていく距離
public function Bird() {
this.x = Math.random() * 465 - 232;
this.y = Math.random() * 465 - 232;
this.mouseEnabled = this.mouseChildren = false;
draw();
}
/**
* 初期描画
*/
private function draw():void {
this.graphics.beginFill(myColor);
this.graphics.moveTo(0, -3);
this.graphics.lineTo(5, 5);
this.graphics.lineTo(-5, 5);
this.graphics.lineTo(0, 3);
this.graphics.endFill();
}
/**
* 座標更新
*/
public function update():void {
var targetVelocity:Number = AVE_V;
var targetAngle:Number = Math.atan2( centerY - this.y, centerX - this.x );
if ( nearestD < AVOID_DISTANCE ) { // 避ける
var nearestAngle:Number = Math.atan2( nearestBird.y - y, nearestBird.x - x ) - angle;
nearestAngle %= 2 * Math.PI;
var tempAngle:Number = (nearestAngle - angle) % ( 2 * Math.PI );
if ( tempAngle < 0 ) tempAngle += 2 * Math.PI;
if ( nearestAngle < (0.5 * Math.PI) && nearestAngle > ( -0.5 * Math.PI ) ) {
targetVelocity = MIN_V;
if ( tempAngle < Math.PI ) targetAngle = angle + AVOID_ANGLE;
else targetAngle = angle - AVOID_ANGLE;
} else {
targetVelocity = MAX_V;
if ( tempAngle < Math.PI ) targetAngle = angle + AVOID_ANGLE;
else targetAngle = angle - AVOID_ANGLE;
}
} else if ( nearestD < FOLLOW_DISTANCE ) { // そろえる
targetVelocity = 0.8 * ( nearestBird.velocity ) + 0.2 * targetVelocity;
targetAngle = (Math.random()<FOLLOW_RATE)?nearestBird.angle:targetAngle;
}
this.velocity += V_RATE * (targetVelocity - this.velocity);
targetAngle -= angle;
while ( targetAngle > Math.PI ) targetAngle -= 2 * Math.PI;
while ( targetAngle < -Math.PI ) targetAngle += 2 * Math.PI;
angle += A_RATE * targetAngle;
this.x += Math.cos(angle) * velocity;
this.y += Math.sin(angle) * velocity;
this.rotation = angle * 180 / (Math.PI);
/*
// デバッグ用 一番近いやつに線を引く ( rotation = 0で固定すること )
this.graphics.clear();
draw();
this.graphics.lineStyle(0, 0xFFFFFF);
this.graphics.moveTo(0, 0);
this.graphics.lineTo(nearestBird.x - this.x, nearestBird.y - this.y);
*/
}
}