forked from: 鳥の群れみたいだった何かを増量

by swfgeek forked from 鳥の群れみたいだった何かを増量 (diff: 1)
♥0 | Line 249 | Modified 2009-11-27 01:36:36 | MIT License
play

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