ニコニコテレビちゃんを群がらせてみた
see http://www.vergenet.net/~conrad/boids/pseudocode.html
// forked from nitoyon's ニコニコテレビちゃんを動かしてみた
// forked from uniq's ニコニコテレビちゃんを描いてみた
// see http://www.vergenet.net/~conrad/boids/pseudocode.html
package{
import flash.display.Sprite;
import flash.utils.*;
public class NicoBoids extends Sprite {
public function NicoBoids() {
var boids:Array = [];
for(var i:int = 0; i < 30; i++){
var body:Body = new Body(0);
var bodyWrapper:Sprite = new Sprite();
bodyWrapper.addChild(body);
body.y = -80;
bodyWrapper.scaleX = bodyWrapper.scaleY = 0.3;
addChildAt(bodyWrapper, 0);
var boid:Boid = new Boid(bodyWrapper, boids);
boids.push(boid);
}
addEventListener("enterFrame", function(event:*):void{
for(i = 0; i < boids.length; i++){
boid = boids[i];
boid.update(stage.mouseX, stage.mouseY);
}
});
}
}
}
import flash.display.Sprite;
import flash.filters.GlowFilter;
import flash.geom.Vector3D;
class Boid {
public function Boid(sprite:Sprite, boids:Array) {
this.sprite = sprite;
this.boids = boids;
// initialize position
position = new Vector3D(
Math.random() * 100,
Math.random() * 100,
0);
// initialize velocity
velocity = new Vector3D(0, 0, 0);
// flocking parameter
minDist = 10;
minDistSq = minDist * minDist;
maxVelocity = 10;
}
public function update(mouseX:Number, mouseY:Number):void {
var cohesion:Vector3D = getCohesion();
var separation:Vector3D = getSeparation();
var alignment:Vector3D = getAlignment();
var targetting:Vector3D = getTargetting(new Vector3D(mouseX, mouseY, 0));
velocity = velocity.add(cohesion)
.add(separation)
.add(alignment)
.add(targetting);
if (velocity.lengthSquared > maxVelocity * maxVelocity) {
velocity.scaleBy(maxVelocity / velocity.length);
}
position = position.add(velocity);
sprite.x = position.x;
sprite.y = position.y;
//sprite.rotation = Math.atan2(velocity.y, velocity.x) * 180 / Math.PI + 90;
}
// Boids try to fly towards the centre of mass of neighbouring boids.
protected function getCohesion():Vector3D {
var pc:Vector3D = new Vector3D();
for (var i:uint = 0; i < boids.length; i++) {
if (this == boids[i]) { continue; }
pc = pc.add(boids[i].position);
}
pc.scaleBy(1.0 / (boids.length - 1));
pc = pc.subtract(position);
pc.scaleBy(0.01);
return pc;
}
// Boids try to keep a small distance away from other objects (including other boids).
protected function getSeparation():Vector3D {
var c:Vector3D = new Vector3D();
for (var i:uint = 0; i < boids.length; i++) {
if (this == boids[i]) { continue; }
var dist:Vector3D = boids[i].position.subtract(position);
if (dist.lengthSquared >= minDistSq) { continue; }
c = c.subtract(dist);
}
return c;
}
// Boids try to match velocity with near boids.
protected function getAlignment():Vector3D {
var pv:Vector3D = new Vector3D();
for (var i:uint = 0; i < boids.length; i++) {
if (this == boids[i]) { continue; }
pv = pv.add(boids[i].velocity);
}
pv.scaleBy(1.0 / (boids.length - 1));
pv = pv.subtract(velocity);
pv.scaleBy(1.0 / 8);
return pv;
}
// Tendency towards a particular place
protected function getTargetting(target:Vector3D):Vector3D {
target = target.subtract(position);
target.scaleBy(1.0 / 100);
return target;
}
public var sprite:Sprite;
public var velocity:Vector3D;
public var position:Vector3D;
public var minDist:Number;
public var minDistSq:Number;
public var maxVelocity:Number;
public var boids:Array;
}
class Body extends Sprite{
public function Body(num:int){
var color:uint = 0x000000 + 0x111111 * num;
var l1:Leg = new Leg();
l1.x = -30; l1.y = 150
addChild(l1);
var l2:Leg = new Leg();
l2.x = 30; l2.y = 150
addChild(l2);
// アンテナを描く
var antena:Sprite = new Sprite();
antena.graphics.lineStyle(4, color);
antena.graphics.moveTo(-30, 40);
antena.graphics.lineTo( 0, 60);
antena.graphics.lineTo( 30, 40);
antena.filters = [new GlowFilter(0xffffff)];
addChild(antena);
// 外側四角を描く
var s2:Sprite = new Sprite();
s2.graphics.lineStyle(6, color);
s2.graphics.beginFill(0xffffff);
s2.graphics.drawRect(-60, 0, 120, 90);
s2.graphics.endFill();
s2.y = 60;
addChild(s2);
// 内側四角を描く
var s3:Sprite = new Sprite();
s3.graphics.lineStyle(4, color);
s3.graphics.beginFill(0xffffff);
s3.graphics.drawRect(-50, 0, 100, 70);
s3.graphics.endFill();
s3.y = 70;
addChild(s3);
// 左目を描く
var eye1:Sprite = new Sprite();
eye1.graphics.beginFill(color);
eye1.graphics.drawCircle(0, 0, 5);
eye1.x = -30;
eye1.y = 100;
addChild(eye1);
// 右目を描く
var eye2:Sprite = new Sprite();
eye2.graphics.beginFill(color);
eye2.graphics.drawCircle(0, 0, 5);
eye2.x = 30;
eye2.y = 90;
addChild(eye2);
// 口を描く
var s8:Sprite = new Sprite();
s8.graphics.lineStyle(1, color);
s8.graphics.beginFill(color);
s8.graphics.moveTo( 0, 120);
s8.graphics.lineTo(-10, 130);
s8.graphics.lineTo( 15, 130);
s8.graphics.endFill();
addChild(s8);
var count:int = num * 2;
addEventListener("enterFrame", function(event:*):void{
l1.rotation = 15 * Math.cos(count / 10 * Math.PI);
l2.rotation = -l1.rotation;
antena.scaleX = Math.cos(count / 30 * Math.PI);
eye1.y = 95 + Math.cos(count / 30 * Math.PI) * 5;
eye2.y = 95 - Math.cos(count / 30 * Math.PI) * 5;
count = (count + 1) % 360;
});
}
}
class Leg extends Sprite{
public function Leg(){
graphics.lineStyle(4, 0x000000);
graphics.beginFill(0xffffff);
graphics.moveTo( 20, -12);
graphics.lineTo( 0, 12);
graphics.lineTo(-20, -12);
graphics.endFill();
}
}