Constellation – A flocking experiment

In: Actionscript|Classes and Functions|Experiments|Flash|Portfolio|Source Code

21 May 2010

This week I saw Flight 404′s “Swarm Behavior” on vimeo –

Swarm behavior from flight404 on Vimeo.

Yet another awesome video from Robert, using his Cinder framework. Cool!
In the description, he mentions the particle’s behavioral rules –

1) If I am far away from my neighbors, move towards them.
2) If I am too close to my neighbors, move away from them.
3) If I am neither too close or too far from my neighbors, move with them.

So I decided to try and implement these rules in Flash. The idea changed a few times, and ended up looking like an interactive constellation app. So here it is! It’s still pretty rough around the edges – but I’m quite pleased with how it’s developing.

See and edit the code over on Wonderfl

CODE

?View Code ACTIONSCRIPT
package{
 
	[SWF(width="465", height="465", frameRate="50")]
 
	import flash.events.*;
	import flash.events.KeyboardEvent;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.Stage;
	import flash.geom.Point;
	import flash.filters.BlurFilter;
	import flash.filters.ColorMatrixFilter;
	import flash.filters.ConvolutionFilter;
	import flash.filters.GlowFilter
	import flash.text.TextField;
	import com.bit101.components.*;
 
 	public class BFCWonderfl extends MovieClip {
 
		//Vars - 
		//You can play with these ones - 
		private var numBoids:int=20;
		//Boids closer than this will feel crowded!
		private var minDist:int=90;
		//Boid further away than another than this will be lonely!
		private var maxDist:int=100;
		//This is how fast the boids can move. TOP SPEED!
		private var maxSpeed:Number = 3;
		//This is how much boids influence each other - lower number = more influence
		private var divideBy:int=100;
		//Shall the boids slow down with friction?
		private var enableFriction:Boolean = true;
		private var friction:Number = 0.9;
		//Settle down - if they're not too close and not too far away from their friends - they'll just stop
		private var settleDown:Boolean = false;
 
		private var boidColour:uint = 0xffffff;
		private var lineColour:uint = 0xffffff;
		private var bgColour:uint   = 0x000033;
 
		private var glow:GlowFilter = new GlowFilter();
		private var glowColour:uint = 0x99ffff;
 
		//But you should probably leave these as is - 
		private var boidArray:Array=[];
		private var menuHeight:Number = 85;
		private var doMidi:Boolean = false;
		private var BMD:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight-menuHeight, false, bgColour);
		private var BF:BlurFilter = new BlurFilter(5,5,1);
		private var Bit:Bitmap = new Bitmap(BMD);
		private var bitHolder:MovieClip = new MovieClip;
		private var ballHolder:MovieClip = new MovieClip;
		private var isMouseDown:Boolean = false;
		private var TB:TextField = new TextField();
		private var myCheckBox1:CheckBox;
		private var myCheckBox2:CheckBox;
		private var mySlider1:HSlider;
		private var mySlider2:HSlider;
		//Boid Flock Class
		public function BFCWonderfl():void{
			trace("Init!");
			addChild(bitHolder);
			bitHolder.addChild(Bit);
			addChild(ballHolder);
 
			glow.color = glowColour;
			glow.alpha = .5;
			glow.blurX = 5;
			glow.blurY = 5;
 
			ballHolder.filters = [glow];
 
			addChild(TB);
			TB.y = 0;
			TB.width = stage.stageWidth-10;
			TB.height = 75;
			TB.selectable = false;
			TB.text = "Constellation by Lawrie\nhttp://www.LawrieCape.co.uk\nSpace to add a boid at mouse - click to attract - any other key to reset.\n\nADD MINIMAL COMPS HERE - MIN MAX sliders and Friction Settle checkboxes";
 
			myCheckBox1 = new CheckBox(this,5,50,"Friction?",updateVals);
			myCheckBox1.selected = true;
			myCheckBox2 = new CheckBox(this,5,70,"Settle?",updateVals);
			var sLabel:Label  = new Label(this,75,45,"Min size");
			var sLabel2:Label = new Label(this,75,65,"Max size");
 
			mySlider1 = new HSlider(this,125,50,updateVals);
			mySlider2 = new HSlider(this,125,70,updateVals);
 
			mySlider1.setSliderParams(1,200,75);
			mySlider2.setSliderParams(1,200,100);
 
			addChild(myCheckBox1);
			addChild(myCheckBox2);
 
			addChild(mySlider1);
			addChild(mySlider2);	
 
			bitHolder.y = menuHeight;
			ballHolder.y = menuHeight;
			ballHolder.mouseEnabled  = false;
			ballHolder.mouseChildren = false;
			bitHolder.alpha=1;
 
			BMD.fillRect(BMD.rect, bgColour);
 
			bitHolder.addEventListener(MouseEvent.MOUSE_DOWN, mouseIsDown);
			bitHolder.addEventListener(MouseEvent.MOUSE_UP,   mouseIsNotDown);			
			stage.addEventListener(KeyboardEvent.KEY_UP, makeASingleBoid);
 
			addEventListener(Event.ENTER_FRAME, updateBoids);
			//makeBoids(numBoids);
		}
 
		private function makeASingleBoid(e:KeyboardEvent):void{
			if(e.keyCode==32){
				makeBoids(1,mouseX,mouseY);
			}
			else{
				reset();
			}
		}
 
		private function mouseIsDown(e:Event):void{
			isMouseDown = true;
		}
		private function mouseIsNotDown(e:Event):void{
			isMouseDown = false;
		}
 
		private function makeBoids(makeXBoids:int, bX:Number = 0, bY:Number = 0):void {
			for (var i:int=0; i<makexboids ; i++) {
				var B:MovieClip = new MovieClip();
				var bInner:MovieClip = new MovieClip();		
 
				//bInner.alpha=0.2;
				bInner.graphics.beginFill(boidColour);
				bInner.graphics.drawCircle(0,0,2);
 
				B.addChild(bInner);
 
				if(bX!=0){
					B.x=bX;
				}
				else{
					B.x=Math.random()*stage.stageWidth;
				}
				if(bY!=0){
					B.y=bY-menuHeight;
				}
				else{
					B.y=Math.random()*stage.stageHeight;
				}
 
				B.speedX = 0.1;
				B.speedY = 0.1  ;
				B.mouseEnabled = false;
				B.mouseChildren = false;
				boidArray.push(B);
				ballHolder.addChild(B);
			}
		}
 
		//Move - 
		private function updateBoids(e:Event):void {
			for (var i:int=0; i<boidArray.length; i++) {
				var B:*=boidArray[i];
				var nearestBoid:*;
				var nearestBoidNum:*;				
				var leastDist:int=1000;
 
				B.distToB2X=0;
				B.distToB2Y=0;
				B.distToB2=0;
 
				if(isMouseDown){
					//Dont bother checking all the boids
				}
				else{
					//This is not the best way of checking all the boids I'm sure, but it works fine for this little demo.
					for (var j:int=0; j<boidArray.length; j++) {
						var B2:*=boidArray[j];
						if (B!=B2) {
							var distX:Number=Math.round(B.x-B2.x);
							var distY:Number=Math.round(B.y-B2.y);
							var dist:Number  = Math.round(Math.sqrt(((distX*distX)+(distY*distY))));
 
							if (dist<leastDist) {
 
								B.distToB2X=distX;
								B.distToB2Y=distY;
								B.distToB2=dist;
 
								leastDist=dist;
								nearestBoid=B2;
								nearestBoidNum = j;
							}
						}
					}
				}
 
				B.graphics.clear();
				B.graphics.lineStyle(1,lineColour,0.5);
 
				if(isMouseDown){					
					B.graphics.lineTo(mouseX-B.x,mouseY-(B.y+menuHeight));
					B.distToB2X= Math.round(B.x-mouseX);
					B.distToB2Y= Math.round((B.y+menuHeight)-mouseY);
					B.distToB2 = Math.round(Math.sqrt(((B.distToB2X*B.distToB2X)+(B.distToB2Y*B.distToB2Y))));
				}
				else{
 
					B.previousBoid = nearestBoid;
 
					try{
						B.graphics.lineTo(nearestBoid.x-B.x,nearestBoid.y-B.y);
					}
					catch(e:Error){
						//Mouse down? Not enough Boids?
					}
				}
 
				//1) If I am far away from my neighbors, move towards them.
				if (B.distToB2>maxDist) {
					B.speedX-=B.distToB2X/divideBy;
					B.speedY-=B.distToB2Y/divideBy;
				}
				//2) If I am too close to my neighbors, move away from them.
				else if (B.distToB2<mindist ){
					B.speedX+=B.distToB2X/divideBy;
					B.speedY+=B.distToB2Y/divideBy;
				}
				//3) If I am neither too close or too far from my neighbors, move with them.
				else if(settleDown){
					//It's cool guys - we got away from them
					B.speedX = 0;
					B.speedY = 0;					
				}
 
				//Bounce off the edges of the stage - 
				if (B.x>stage.stageWidth){
					B.x=stage.stageWidth-B.speedX;
					B.speedX*=-friction;
				}
				else if(B.x&lt;0){
					B.x=-B.speedX;
					B.speedX*=-friction;
				}
				if (B.y>(stage.stageHeight-menuHeight)){
					B.y=(stage.stageHeight-menuHeight)-B.speedY;
					B.speedY*=-friction;
				}
				else if(B.y&lt;0){
					B.y=-B.speedY;
					B.speedY*=-friction;
				}
 
				//Limit them to a top speed - 
				if(B.speedX>maxSpeed){
					B.speedX = maxSpeed;
				}
				else if(B.speedX< -maxSpeed){
					B.speedX=-maxSpeed;
				}
				if(B.speedY>maxSpeed){
					B.speedY = maxSpeed;
				}
				else if(B.speedY< -maxSpeed){
					B.speedY=-maxSpeed;
				}		
 
				//Add friction?
				if(enableFriction){
					B.speedX*=friction;
					B.speedY*=friction;		
				}
 
				//Move them 
				B.x+=B.speedX;
				B.y+=B.speedY;
 
			}
			//Draw into the BMD
 
			BMD.draw(ballHolder);
			//Apply a blur?
			BMD.applyFilter(BMD,BMD.rect,new Point(0,0),BF);
		}
 
		private function reset():void{
			removeEventListener(Event.ENTER_FRAME, updateBoids);
			for (var i:int=0; i<boidArray.length; i++) {
				var B:*=boidArray[i];
 
					B.parent.removeChild(B);
					B = null;
			}
			BMD.fillRect(BMD.rect, bgColour);
			boidArray = [];
			addEventListener(Event.ENTER_FRAME, updateBoids);
		}
 
		private function updateVals(e:Event):void{
			//update!
			enableFriction = myCheckBox1.selected;
			settleDown = myCheckBox2.selected;
			minDist = mySlider1.value;
			maxDist	= mySlider2.value;
		}
 
		private function map(v:Number, a:Number, b:Number, x:Number = 0, y:Number = 1):Number {
		    return (v == a) ? x : (v - a) * (y - x) / (b - a) + x;
		}
	}
}

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • StumbleUpon
  • Tumblr
  • Twitter

1 Response to Constellation – A flocking experiment

Avatar

Karin

November 25th, 2010 at 3:58 pm

have you seen the hypeframework from Joshua Davis and Branden Hall? Also swarm, but also lots of other features. Its amazing to work with. http://www.hypeframework.org

Comment Form

About this blog

This is the blog of Lawrie Cape, an interactive developer from Leeds, England.

Photostream

  • COne
  • CTwo
  • CThree
  • CFour
  • Lawrie: Hi Dinaz, I'm not sure what's happening on your system I'm afraid. When you run the FlashMidiServer [...]
  • Dinaz Kardooni: Hi, I am having trouble with the Flash Midi Server. I downloaded it and it says it is connected, but [...]
  • It’s Movember again! - The Lawrie Cape Blog: [...] was a great project to work on and I wrote a blog post about it here. We had a nice surprise t [...]
  • Lawrie: Glad you got it working :) [...]
  • Anonymous: Yeah, had to do this first: var mc:MovieClip = MovieClip(this.parent); trace(map(mc.mouseY, 0, s [...]

Donate

Found any helpful code? Why not donate a quid or two. Cheers!