Flash, Actionscript, Design, Games and Programming.
In: Actionscript|Classes and Functions|Experiments|Flash|Portfolio|Source Code
21 May 2010This 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
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<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<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; } } } |
This is the blog of Lawrie Cape, an interactive developer from Leeds, England.
1 Response to Constellation – A flocking experiment
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