Boid
/**
* Copyright termat ( http://wonderfl.net/user/termat )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/nALB
*/
package
{
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.geom.Rectangle;
[SWF(width=480,height=480,backgroundColor=0x000000,frameRate=60)]
public class Practice23 extends Sprite {
private var bmpdata:BitmapData;
private var bmp:Bitmap;
private var colortrans:ColorTransform;
private var filter:BlurFilter;
private var aq:Aquarium;
private var isDown:Boolean = false;
private var pos:Point = new Point(0, 0);
public function Practice23():void{
bmpdata = new BitmapData( stage.stageWidth, stage.stageWidth, false, 0x000000);
bmp = new Bitmap(bmpdata);
addChild(bmp);
colortrans = new ColorTransform( 0.95, 0.99, 0.99 );
filter = new BlurFilter(2, 2, 1);
aq = new Aquarium(480, 480, 50);
addEventListener(Event.ENTER_FRAME, update);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
}
private function update(e:Event):void {
bmpdata.lock();
bmpdata.applyFilter( bmpdata, bmpdata.rect, bmpdata.rect.topLeft, filter );
bmpdata.colorTransform( bmpdata.rect, colortrans );
aq.draw(bmpdata);
bmpdata.draw(this);
bmpdata.unlock();
if (isDown) {
aq.addBoid(pos.x,pos.y);
}
}
private function mouseDown(e:MouseEvent):void {
isDown = true;
pos.x = e.stageX;
pos.y = e.stageY;
}
private function mouseUp(e:MouseEvent):void {
isDown = false;
}
private function mouseMove(e:MouseEvent):void {
if (isDown) {
pos.x = e.stageX;
pos.y = e.stageY;
}
}
}
}
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.display.BitmapData;
class Aquarium {
private var bounds:Rectangle;
public var list:Array;
public function Aquarium(w:Number, h:Number, boidnum:int) {
bounds=new Rectangle(0,0,w,h);
Boid.aq = this;
list = new Array();
for (var i:int = 0; i < boidnum; i++) {
addBoid(bounds.width * Math.random(),bounds.height * Math.random());
}
}
public function draw(bmpdata:BitmapData):void {
for each(var p:Boid in list) {
p.setFriends(list);
}
for each(p in list) {
p.update();
bmpdata.setPixel( p.x, p.y, p.color );
bmpdata.setPixel( (p.x * 0.9 + p.senser.x * 0.1), (p.y * 0.9 + p.senser.y * 0.1), p.color );
bmpdata.setPixel( (p.x*0.8+p.senser.x*0.2), (p.y*0.8+p.senser.y*0.2), p.color );
}
}
public function addBoid(xx:Number, yy:Number):void {
if (list.length > 300) list.shift();
var b:Boid = new Boid();
b.x = xx;
b.y = yy;
b.dir = Math.PI * Math.random();
list.push(b);
}
public function isOutOfBounds(p:Point, a:Boid):Boolean {
return !bounds.containsPoint(p) || !bounds.contains(a.x, a.y);
}
public function isCollision(a:Boid):Boolean{
for each(var o:Boid in list){
if(a==o)continue;
if (o.distance(a) <= a.size) {
var tmp:Number = o.senser.x * a.senser.x + o.senser.y * a.senser.y;
return tmp < 0;
}
}
return false;
}
}
class Boid {
private const MAX_SPEED:Number = 3.2;
private const MIN_SPEED:Number = 0.8;
private const MAX_ACCEL:Number = 0.8;
private const MAX_TURN:Number = 0.06;
private const VIEW_DIST:Number = 50;
private const VIEW_RAD:Number = 140.0/180.0*Math.PI;
private const TWO_PI:Number=2.0*Math.PI;
private const HALF_PI:Number=Math.PI/2.0;
private const MIN_DIST:Number=30.0;
public static var aq:Aquarium=null;
public var x:Number;
public var y:Number;
public var speed:Number=0.0;
public var dir:Number=0.0;
private var friends:Vector.<Boid>;
public var size:Number = 40.0;
public var color:uint = 0xffffff*Math.random();
public var senser:Point;
public var gene:Number;
public function Boid():void{
friends = new Vector.<Boid>();
senser = new Point(0, 0);
if (Math.random() <= 0.5) {
gene=1.0;
}else {
gene = -1.0;
}
}
public function numOfFriends():int {
return this.friends.length;
}
public function distance(a:Boid):Number{
var xx:Number=a.x-this.x;
var yy:Number=a.y-this.y;
return Math.sqrt(xx*xx+yy*yy);
}
private function alignment():void {
if(friends.length<1) return;
var avg:Number = 0;
for each(var a:Boid in friends) {
avg += a.speed;
}
avg /= friends.length;
if(speed<avg){
speed +=MAX_ACCEL;
}else{
speed -=MAX_ACCEL;
}
speed = Math.max(MIN_SPEED, speed);
}
private function cohesion():void {
if(friends.length<1){
speed = Math.min(MAX_SPEED, speed + MAX_ACCEL);
return;
}
var ax:Number = 0;
var ay:Number = 0;
for each(var a:Boid in friends) {
ax += a.x;
ay += a.y;
}
ax /=friends.length;
ay /=friends.length;
var center:Number=Math.atan2(ay-y,ax-x);
if(center<0)center=TWO_PI+center;
center=center+HALF_PI-dir+VIEW_RAD/2;
center = center % TWO_PI;
if(center<0)center +=TWO_PI;
if(center>VIEW_RAD/2.0){
dir +=MAX_TURN;
}else{
dir -=MAX_TURN;
}
}
private function separation():void {
if (friends.length < 1) return;
var minlen:Number = 1000000;
for each(var a:Boid in friends) {
minlen=Math.min(minlen,this.distance(a));
}
if(minlen<MIN_DIST){
speed =Math.max(MIN_SPEED,speed-MAX_ACCEL);
}
}
private function avoidance():void{
updateSenser();
if(aq.isOutOfBounds(senser,this)){
dir +=MAX_TURN*gene;
}else if(aq.isCollision(this)){
dir -=MAX_TURN*gene;
}
}
private function updateSenser():void {
senser.x = x + size * Math.cos(dir);
senser.y = y + size * Math.sin(dir);
}
public function update():void {
separation();
cohesion();
alignment();
updateSenser();
avoidance();
if (dir > TWO_PI) {
dir -= TWO_PI;
}else if (dir < 0) {
dir += TWO_PI;
}
x += speed * Math.cos(dir);
y += speed * Math.sin(dir);
}
public function setFriends(list:Array):void {
while (friends.length > 0) friends.pop();
for (var i:int = 0; i < list.length; i++) {
if (list[i] == this) continue;
checkFriend(list[i]);
}
}
private function checkFriend(target:Boid):void {
var len:Number = distance(target);
if (len<=VIEW_DIST) {
var a:Number=Math.atan2(target.y-this.y, target.x-this.x);
if(a<0)a=TWO_PI+a;
a = (a + HALF_PI - this.dir + VIEW_RAD / 2)%TWO_PI;
if (a < 0) a += TWO_PI;
if (a >= 0 && a <= VIEW_RAD) {
friends.push(target);
}
}
}
}