Actionscript 3 - Fun with 3D Particles
Hi there, some time since I last posted a technical post so here we are.
Brief
I was experimenting with some simple 3d formulas and this is the result…
1200 particles morphing in 3D space.
Added an interactive camera to start having fun.
Usage
Simply move your mouse up/down to move camera forward/backward and left/right for strafing.
Perss SPACE bar to cycle particle conformation between four different shapes.
For better experience move forward and backward while particles are morphing…
Following the morph from behind while moving very fast feel very gratifing for me
Performances
On my old CPU it performs @31fps and neither takes the cpu at 100%
No z-sorting here, just fun. I am not trying to build a real 3d engine… and no class for now, just some procedural code, but I am sure my cool readers will not have problems wrapping it inside a class definition if needed
Caurina Tweener needed.
And here come the code! Simply past it in the first frame actions of a new fla.
import caurina.transitions.Tweener; import com.oaxoa.components.FrameRater; import flash.events.KeyboardEvent; var particles:Array=[]; var particlesXY:Array=[]; var flen:Number=100; var lines:Sprite=new Sprite(); var renderLines:Boolean=false; var shapeN:int=-1; var camera:Object={px:0, py:0, pz:0}; function drawBg():void { var bg:Sprite=new Sprite(); bg.graphics.beginFill(0x333333); bg.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight); bg.graphics.endFill(); addChild(bg); var tf:TextFormat=new TextFormat(); tf.color= 0x999999; tf.font="_sans"; tf.size=11; tf.align="right"; var t:TextField=new TextField(); t.width=300; t.defaultTextFormat=tf; t.text="Press SPACEBAR to change particles shape"; t.x=stage.stageWidth-t.width-10; t.selectable=false; bg.addChild(t); } function addParticle(x:Number, y:Number, z:Number):void { var p:MovieClip=new MovieClip(); p.graphics.beginFill(0xcccccc); p.graphics.drawCircle(0, 0, 5); p.graphics.endFill(); p.px=x; p.py=y; p.pz=z; particles.push(p); } function cycleShape():void { if (shapeN<3) { shapeN++; } else { shapeN=0; } var i:uint; var p:MovieClip; var px:Number; var py:Number; var pz:Number; var arad:Number; if (shapeN==0) { var a:Number=0; for (i=0; i<particles.length; i++) { a+=9; a%=360; p=particles[i]; arad=a/180*Math.PI; px=Math.cos(arad)*100; py=Math.sin(arad)*100; pz=Math.floor(i/40)*60; Tweener.addTween(p, {time:2, transition:"EaseInOutCubic", px:px, py:py, pz:pz, delay:i/300}); } } else if (shapeN==1) { for (i=0; i<particles.length; i++) { p=particles[i]; arad=i/180*Math.PI; arad*=2; px=Math.sin(arad)*100; py=Math.cos(arad)*100; pz=i; Tweener.addTween(p, {time:2, transition:"EaseInOutElastic", px:px, py:py, pz:pz, delay:i/300}); } } else if (shapeN==2) { for (i=0; i<particles.length; i++) { p=particles[i]; px=Math.random()*700-350; py=Math.random()*700-350; pz=Math.random()*1700; Tweener.addTween(p, {time:2, transition:"EaseInOutCubic", px:px, py:py, pz:pz, delay:i/200}); } } else if (shapeN==3) { var row:uint=0; var col:uint=0; for (i=0; i<particles.length; i++) { col=(i%50); row=Math.floor(i/50); p=particles[i]; px=0 py=-450+row*40; pz=col*40; Tweener.addTween(p, {time:2, transition:"EaseInOutBounce", px:px, py:py, pz:pz, delay:i/200}); } } } function render():void { var c:uint=0; for each (var p:MovieClip in particles) { var dx:Number=p.px-camera.px; var dy:Number=p.py-camera.py; var dz:Number=p.pz-camera.pz; var scale:Number=flen/(flen+(dz)); if (scale<0) { scale=0; } p.x=stage.stageWidth/2+dx*scale; p.y=stage.stageHeight/2+dy*scale; p.scaleX=p.scaleY=scale; particlesXY[c]={x:p.x, y:p.y, scale:scale}; c++; } // activate lines render, false by default, not really a wireframe if (renderLines) { lines.graphics.clear(); lines.graphics.lineStyle(2, 0xCCCCCC); for(var i:uint=0; i<particlesXY.length; i++) {; var t:Object=particlesXY[i]; if (i<particlesXY.length-1 && t.scale>0) { var t2:Object=particlesXY[i+1]; lines.graphics.moveTo(t.x, t.y); lines.graphics.lineTo(t2.x, t2.y); } } } } stage.addEventListener(KeyboardEvent.KEY_UP, onkey); function onkey(event:KeyboardEvent):void { if(event.keyCode==32) { cycleShape(); render(); } } addEventListener(Event.ENTER_FRAME, onframe); function onframe(event:Event):void { var offx:Number=stage.stageWidth/2-mouseX; camera.px-=offx/5; if (camera.px>150) { camera.px=150; } if (camera.px<-150) { camera.px=-150; } var offy:Number=stage.stageHeight/2-mouseY; if(offy>70) offy=70; if(offy<-70) offy=-70; camera.pz+=offy/5; if (camera.pz>1700) { camera.pz=1700; } if (camera.pz<0) { camera.pz=0; } render(); } function addSprites():void { for each (var p:MovieClip in particles) { addChildAt(p, 1); } } function init():void { for(var i:uint=0; i<1200; i++) { var px:Number=0; var py:Number=0; var pz:Number=0; addParticle(px, py ,pz); } } // main methods call drawBg(); addChild(lines); init(); addSprites(); cycleShape(); render(); var fr:FrameRater=new FrameRater(0xffffff); addChild(fr);
Have fun and leave a comment if you like.












































_phase_O said
am April 3 2008 @ 4:14 pm
Pretty cool!
Fardeen said
am April 4 2008 @ 8:39 am
Excellent ! love it !
Jonas said
am April 4 2008 @ 8:18 pm
you feel tweener is better than for example tweenLite? and if u do, why?
Pierluigi Pesenti said
am April 7 2008 @ 5:15 pm
eheh I don’t feel Tweener is better, it’s simply the only tweeneing class I tested so far
I have just seen some samples of the new TweenMax and seems really cool. In the next few days I will do some test with it and eventually migrate
Paul said
am April 9 2008 @ 4:29 pm
Oh man, I’m glad I found your blog today - good work.
I’ve added you to my RSS feed
Dark Vyper said
am April 15 2008 @ 1:42 pm
Nice example. It took a while for me to understand the source - nice code too!
benje said
am July 18 2008 @ 11:52 pm
I love what you’ve done here, beautiful effect!