In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

鳥の群れみたいな何か

/**
 * 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);
		*/
	}
}