forked from: 鳥の群れみたいだった何かを増量
forked from 鳥の群れみたいだった何かを増量 (diff: 1)
ActionScript3 source code
/**
* Copyright swfgeek ( http://wonderfl.net/user/swfgeek )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/vAXn
*/
// forked from keno42's 鳥の群れみたいだった何かを増量
// forked from keno42's 鳥の群れみたいな何か 数を5倍に増やした
package
{
import com.bit101.components.HSlider;
import com.bit101.components.Label;
import com.bit101.components.Panel;
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 = 350; // 個体数
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(); // えさ場(?)の場所
private var birdGrid:Array;
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);
}
var gui:Sprite = buildGUI();
// addChild( gui );
// 背景は黒くしとく
this.graphics.beginFill(0);
this.graphics.drawRect(0,0,465,465);
// 背景グリッド(無駄に大きめ)
bg.graphics.clear();
bg.graphics.lineStyle(0, 0x00FF00, 0.25);
for ( i = 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);
}
/**
* パラメータいじるためのgui
* @return gui
*/
private function buildGUI():Sprite {
var ret:Sprite = new Sprite();
var panel:Panel = new Panel(ret, 0, 0);
(new HSlider(panel, 0, 0)).addEventListener(Event.CHANGE, changeMaxSpeed);
(new HSlider(panel, 0, 10)).addEventListener(Event.CHANGE, changeMinSpeed);
(new HSlider(panel, 0, 20)).addEventListener(Event.CHANGE, changeAvoidD);
(new HSlider(panel, 0, 30)).addEventListener(Event.CHANGE, changeFollowD);
return ret;
}
/**
* 追従範囲変更
*/
private function changeFollowD(e:Event):void
{
Bird.FOLLOW_DISTANCE = Math.pow(e.target.value + 3,2);
}
/**
* 回避範囲変更
*/
private function changeAvoidD(e:Event):void
{
Bird.AVOID_DISTANCE = Math.pow(e.target.value + 3,2);
}
/**
* 最大速度変更
*/
private function changeMaxSpeed(e:Event):void
{
Bird.MAX_V = e.target.value * 0.1;
Bird.AVE_V = (Bird.MAX_V + Bird.MIN_V) * 0.5 + Bird.MIN_V;
}
/**
* 最低速度変更
*/
private function changeMinSpeed(e:Event):void
{
Bird.MIN_V = e.target.value * 0.1;
Bird.AVE_V = (Bird.MAX_V + Bird.MIN_V) * 0.5 + Bird.MIN_V;
}
/**
* 毎フレーム更新
*/
private function onEnterFrame(e:Event):void {
// えさ場(?)の変更
if ( Math.random() < 0.0025 ) changeTarget();
// 中心位置とか計算用の初期化
center.x = 0;
center.y = 0;
var maxX:Number = -99999999999999999999;
var minX:Number = 99999999999999999999;
var maxY:Number = -99999999999999999999;
var minY:Number = 99999999999999999999;
// 存在範囲の特定
var currentBird:Bird = firstBird;
do {
/*
// 工夫ゼロの2重ループなのでめちゃ重い。O(N^2)
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 )
/*
// デバッグ用 外枠に線を引く
birds.graphics.clear();
birds.graphics.lineStyle(0, 0x00FFFF);
birds.graphics.drawRect(minX, minY, maxX - minX, maxY - minY);
*/
birdGrid = [];
var xNum:int = Math.ceil((maxX - minX) / Math.sqrt(Bird.FOLLOW_DISTANCE)) + 1;
var yNum:int = Math.ceil((maxY - minY) / Math.sqrt(Bird.FOLLOW_DISTANCE)) + 1;
for ( var i:int = 0; i < yNum; i++ ) {
for ( var j:int = 0; j < xNum; j++ ) {
birdGrid.push([]);
}
}
var inverseD:Number = 1.0 / Bird.FOLLOW_DISTANCE;
currentBird = firstBird;
do {
birdGrid[
int((currentBird.x - minX) * inverseD) +
int((currentBird.y - minY) * inverseD) * xNum
].push(currentBird);
currentBird.nearestD = 999999999999999999;
} while ( currentBird = currentBird.next )
// なんとかO(N)に近づいた。運悪く密集したときだけ最悪のO(N^2)
for ( i = 0; i < yNum - 1; i++ ) {
for ( j = 0; j < xNum - 1; j++ ) {
var myGrid:Array = birdGrid[j + i * xNum];
var myGridNum:int = myGrid.length;
for ( var k:int = 0; k < myGridNum; k++ ) {
currentBird = myGrid[k];
// 同グリッド内
for ( var l:int = k + 1; l < myGridNum; l++ ) {
checkDistance(currentBird, myGrid[l]);
}
// 右のグリッド内
var otherGrid:Array = birdGrid[j + 1 + i * xNum];
var otherGridNum:int = otherGrid.length;
for ( l = 0; l < otherGridNum; l++ ) {
checkDistance(currentBird, otherGrid[l]);
}
// 右下のグリッド内
otherGrid = birdGrid[j + 1 + (i + 1) * xNum];
otherGridNum = otherGrid.length;
for ( l = 0; l < otherGridNum; l++ ) {
checkDistance(currentBird, otherGrid[l]);
}
// 下のグリッド内
otherGrid = birdGrid[j + (i + 1) * xNum];
otherGridNum = otherGrid.length;
for ( l = 0; l < otherGridNum; l++ ) {
checkDistance(currentBird, otherGrid[l]);
}
}
}
}
// 座標の加重平均
center.x /= COUNT;
center.y /= COUNT;
// 各自が行動を決定
currentBird = firstBird;
do {
// 常に群れの中央を目指す。蚊柱的になる・・・
// currentBird.centerX = center.x;
// currentBird.centerY = center.y;
// ランダムに指定されたどこかを目指す。
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);
// birds.x += 0.1 * (232 - (minX + 0.5 * (maxX-minX)) - birds.x);
// birds.y += 0.1 * (232 - (minY + 0.5 * (maxY-minY)) - birds.y);
// 背景グリッド描画
drawbg();
}
private function checkDistance(myBird:Bird, otherBird:Bird):void {
var targetX:Number = otherBird.x - myBird.x;
var targetY:Number = otherBird.y - myBird.y;
var distance:Number = targetX * targetX + targetY * targetY;
if ( distance < myBird.nearestD ) {
myBird.nearestD = distance;
myBird.nearestBird = otherBird;
}
if ( distance < otherBird.nearestD ) {
otherBird.nearestD = distance;
otherBird.nearestBird = myBird;
}
}
private function changeTarget():void {
targetPoint.x = Math.random() * 15000 - 7500;
targetPoint.y = Math.random() * 15000 - 7500;
}
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 QuadTree
{
private var isLeaf:Boolean = false;
}
class Bird extends MovieClip
{
public static var MAX_V:Number = 5; // 最大速度
public static var MIN_V:Number = 1; // 最低速度
public static var AVE_V:Number = 0.5 * (MAX_V - MIN_V) + MIN_V;
public var V_RATE:Number = 0.2; // 速度変更時の掛け率
public var A_RATE:Number = 0.15; // 角度変更時の掛け率
public var FOLLOW_RATE:Number = 0.1; // 一番近いのに素直についていく確率
public var AVOID_ANGLE:Number = 1.5; // どのくらいあわてて避けるか?
public var AVOID_RANDOM_VELOCITY: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;
// ランダム有り
// public static const AVOID_DISTANCE:Number = Math.pow( 2 + 3 * Math.random(), 2); // 避ける距離
// public static const FOLLOW_DISTANCE:Number = Math.pow( 6 + 3 * Math.random(), 2); // ついていく距離
// ランダム無し
public static var AVOID_DISTANCE:Number = Math.pow( 7, 2); // 避ける距離
public static var FOLLOW_DISTANCE:Number = Math.pow( 9, 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();
this.scaleX = this.scaleY = 1;
}
/**
* 座標更新
*/
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 - Math.random() * AVOID_RANDOM_VELOCITY;
if ( tempAngle < Math.PI ) targetAngle = angle + AVOID_ANGLE;
else targetAngle = angle - AVOID_ANGLE;
} else {
targetVelocity = MAX_V + Math.random() * AVOID_RANDOM_VELOCITY;
if ( tempAngle < Math.PI ) targetAngle = angle + AVOID_ANGLE;
else targetAngle = angle - AVOID_ANGLE;
}
targetVelocity += Math.random() * AVOID_RANDOM_VELOCITY;
} 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, (nearestD<AVOID_DISTANCE)?0xFF0000:0xFFFFFF);
this.graphics.moveTo(0, 0);
this.graphics.lineTo(nearestBird.x - this.x, nearestBird.y - this.y);
this.graphics.drawCircle(nearestBird.x - this.x, nearestBird.y - this.y, 3);
*/
}
}