Starry Trails - 3D Particle System ActionScript 3 source code
Another example of an AS3 3D Sprite based particle system. This one is using my 3D sprite engine but will port this to papervision asap. See it here.
The source code is by no means generic enough to be considered an engine or library, but is work in progress. I will extend the Node3D, SpringyNode3D classes to use Papervision3D and the Billboards for the rendering, and will definitely carry on working on the particle systems. I've also been converting a lot of noise functions to AS3 and will post them soon... its all getting quite exciting in the flash world! :P
This app is made of quite a few classes so Ill start from the top (application) downwards.
/*********************************************************************** MAIN DOCUMENT CLASS make sure you have 2 sprites in your library exported for actionscript with linkage identifiers set to RedStar and BlueStar ***********************************************************************/ package { import flash.display.*; import msa.sprite3d.*; public class StarryMouse3Dapp extends Sprite { public function StarryMouse3Dapp() { addChild( new ParticleSystem3D(2000, RedStar) ); // create a new ParticleSystem using the RedStar Class and add to display list addChild( new ParticleSystem3D(2000, BlueStar) ); // create a new ParticleSystem using the BlueStar Class and add to display list stage.align = StageAlign.TOP_LEFT; // align stage to top left stage.scaleMode = StageScaleMode.NO_SCALE; // and make flash not scale as it is resized but just extend to cover the new area } } }
The following class is for the ParticleSystem3D. This just takes care of when to emit and update particles. It doesn't actually define individual particle behavior, but is like the container for all particles of a system.
/*********************************************************************** 3D PARTICLE SYSTEM CLASS Manages emitter, and all particles v0.4.0 ----------------------------------- Copyright (c) 2008, Memo Akten, www.memo.tv This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ***********************************************************************/ package msa.sprite3d { import flash.geom.*; import flash.display.*; import flash.events.*; import flash.utils.*; public class ParticleSystem3D extends Sprite { //emission properties: (should group these into an object, would allow flexibility in the future for more generic particle system) internal var gravity:Number = 0.0; // how much gravity there is internal var fadeSpeed:Number = 0.95; // speed at which particles fade out internal var fadeStartTime:int = 1000; // milliseconds after which particles start fading out internal var growSpeed:Number = 0.02; // speed at which particles grow internal var emitSpeed:Number = 5.0; // max speed particles have when emitted internal var inheritSpeed:Number = 0.6; // how much of the emitters speed does the particle inherit internal var minSize:Number = 0.02; // minimum size of the particle internal var maxSize:Number = 0.4; // maximum size of the particle internal var maxRotSpeed:Number = 10; // maximum rotational speed internal var inheritVel:Vector3D; // the actual velocity vector each particle will inherit (updated each frame by the particle system) internal var nowTime:int; // milliseconds since app has been run internal var graphicClass:Class; private var _emitTimer:Timer; // Timer instance to handle when to emit new particles private var _emitter:Emitter3D; // handle to an emitter object public function ParticleSystem3D(freq:Number, graphicClass:Class) { this.graphicClass = graphicClass; _emitter = new Emitter3D(this, graphicClass); addChild(_emitter); _emitter.setPosition((Math.random()-0.5) * 1000, (Math.random()-0.5) * 1000, (Math.random()-0.5) * 1000); setFreq(freq); inheritVel = new Vector3D(); addEventListener(Event.ENTER_FRAME, update); } public function setFreq(freq:Number) { _emitTimer = new Timer(1000/freq); _emitTimer.addEventListener(TimerEvent.TIMER, emit); startEmitter(); } private function emit(e:TimerEvent) { addChild( new Particle3D(this, graphicClass, _emitter.pos.x, _emitter.pos.y, _emitter.pos.z) ); } public function startEmitter() { _emitTimer.start(); } public function stopEmitter() { _emitTimer.stop(); } private function update(e:Event) { nowTime = getTimer(); // get the current time inheritVel = Vector3D.mult(_emitter.vel, inheritSpeed); // calculate velocity inheritance vector for (var i:uint=0; i < numChildren; i++) // loop through all children of the ParticleSystem (all particles and the _emitter object (getChildAt(i) as Node3D).updateAndRender(); // call its updateAndRender function } } }
Next is the Particle3D class. This is the what each of the particles you see on screen are an instance of. Once a particle is emitted into the ParticleSystem3D, they live on their own. The ParticleSystem3D simply calls the update function for each particle (I did this to have more control as opposed to giving each particle its own enterframe handler). The constructor creates the particle at the emitters position with random velocity and other parameters, then the update function defines its behavior.
/*********************************************************************** GENERIC 3D PARTICLE CLASS Takes care of an individual particle v0.4.0 TODO: create superclass for particles so different behaviours can be added quite easily ----------------------------------- Copyright (c) 2008, Memo Akten, www.memo.tv This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ***********************************************************************/ package msa.sprite3d { import flash.utils.* public class Particle3D extends Node3D { private var _age:int; private var _birthTime:int; private var _maxSize:Number; private var pSystem:ParticleSystem3D; public function Particle3D(pSystem:ParticleSystem3D, graphicClass:Class, tx:Number = 0, ty:Number = 0, tz:Number = 0) { super(tx, ty, tz); // call super (Node3D) constructor this.pSystem = pSystem; // save reference to the parent Particle System addChild( new graphicClass ); // add the relevant graphic from the library _birthTime = getTimer(); size.x = size.y = 0.1; scaleX = scaleY = 0; _maxSize = pSystem.minSize + Math.random() * (pSystem.maxSize - pSystem.minSize); alpha = Math.random(); vel = new Vector3D((Math.random()-0.5) * pSystem.emitSpeed, (Math.random()-0.5) * pSystem.emitSpeed, (Math.random()-0.5) * pSystem.emitSpeed); vel = Vector3D.add(vel, pSystem.inheritVel); vr = (Math.random() - 0.5) * pSystem.maxRotSpeed;// rotational velocity rotation = Math.random() * 360; } override public function update() { // the particle behaviour is defined in here _age = pSystem.nowTime - _birthTime; vel.y += pSystem.gravity; if(size.x < _maxSize) size.x = size.y += pSystem.growSpeed; else size.x = size.y = _maxSize; if(_age > pSystem.fadeStartTime) { alpha *= pSystem.fadeSpeed; if(alpha<0.01) parent.removeChild(this); } } } }
The next class is for the emitter. This is what the two big stars orbiting the mouse are instances of. The name of the class could be a bit misleading as it doesn't really take care of emitting particles (the ParticleSystem3D class does that), this class simply defines the physical behavior of the visual emitter object (in this case it behaves as if its orbiting the mouse cursor in 3D while attached to it with a spring - due to it extending the SpringyNode3D class)
/*********************************************************************** SPRINGY 3D PARTICLE EMITTER CLASS v0.4.0 TODO: create superclass for emitters so different behaviours can be added quite easily ----------------------------------- Copyright (c) 2008, Memo Akten, www.memo.tv This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ***********************************************************************/ package msa.sprite3d { public class Emitter3D extends SpringyNode3D { private var _pSystem:ParticleSystem3D; private var _circleFreq:Number= 0.002; private var _circleRadius:Number= 200; public function Emitter3D(_pSystem:ParticleSystem3D, graphicClass:Class) { this._pSystem = _pSystem; addChild( new graphicClass ); // add the relevant graphic from the library } override public function update() { var sx:Number = Math.cos(_pSystem.nowTime * _circleFreq); var sy:Number = Math.sin(_pSystem.nowTime * _circleFreq); var rad:Number = Math.sin(_pSystem.nowTime * _circleFreq * 0.2); setTarget3(_pSystem.mouseX + (Math.random() - 0.5) * 200, _pSystem.mouseY + (Math.random() - 0.5) * 200, 0 + (Math.random() - 0.5) * 200); target.x += sx * _circleRadius * rad; target.z += sy * _circleRadius * rad; vel.z += (Math.random()-0.5) * (vel.x*vel.x + vel.y*vel.y) * 0.01; super.update(); // call the update function of the super class (SpringyNode3D) to do all spring functions } } }
SpringyNode3D is the class which handles the springy behaviour of the 2 big stars.
/*********************************************************************** 3D SPRINGY NODE CLASS Creates a 3D sprite attached to a point in 3D space with spring of zero length and adjustable endpoint, stiffness, damping v0.4.0 ----------------------------------- Copyright (c) 2008, Memo Akten, www.memo.tv This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ***********************************************************************/ package msa.sprite3d { public class SpringyNode3D extends Node3D { internal var target:Vector3D; internal var k:Number = 0.05; // default values internal var d:Number = 0.1; public function SpringyNode3D(tx:Number = 0, ty:Number = 0, tz:Number = 0) { super(tx, ty, tz); target = new Vector3D(); } public function setSpring(k, d) { this.k = k; this.d = d; } public function setTarget(t:Vector3D) { target = t.clone(); } public function setTarget3(x, y, z) { target.set(x, y, z); } override public function update() { // add spring forces to the velocity vel.x += (target.x - pos.x) * k - vel.x * d; vel.y += (target.y - pos.y) * k - vel.y * d; vel.z += (target.z - pos.z) * k - vel.z * d; } } }
The next 2 classes I will scrap in favor of Papervision3Ds billboard rendering when I get a chance. First one is Node3D which simply is a physical node, in 3D space.
/*********************************************************************** GENERIC 3D SPRITE NODE CLASS Renders as a 3D sprite and has basic physical properties like velocity, momentum etc. v0.4.0 TODO: add camera functionilty instead of fixed projection ----------------------------------- Copyright (c) 2008, Memo Akten, www.memo.tv This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ***********************************************************************/ package msa.sprite3d { import flash.display.Sprite; import flash.geom.Point; import flash.geom.ColorTransform; public class Node3D extends Sprite { public var pos:Vector3D; // contains x,y,z position public var vel:Vector3D; // contains velocity vector public var size:Point; // the x and y size of the object public var oldPos:Vector3D; // the old position public var drag:Number; // a kind of fake/simplified friction public var vr:Number; // rotational velocity static var MAX_Z:Number = 400; // distance of camera to the front plane, decreasing this increases the FOV. Increasing it decreases the FOV. private var invZ:Number; // used for internal calculations public function Node3D(tx:Number = 0, ty:Number = 0, tz:Number = 0) { size = new Point(1, 1); pos = new Vector3D(tx, ty, tz); vel = new Vector3D(0, 0, 0); } public function setPosition(tx:Number = 0, ty:Number = 0, tz:Number = 0) { pos.set(tx, ty, tz); } public function updateAndRender() { // transforms the 3D position/scale etc. to 2D stage coordinates update(); oldPos = pos.clone(); // copy position to old position pos = Vector3D.add(pos, vel); // add velocity to position if(stage && pos.z > -MAX_Z) { // if the sprite is beyong the front clipping plane and should be rendered; invZ = MAX_Z/(MAX_Z + pos.z); // calculate transformation amount var ct:ColorTransform = transform.colorTransform; ct.redMultiplier = ct.greenMultiplier = ct.blueMultiplier = invZ; // applies black fog, can be changed to any ColorTransform transform.colorTransform = ct; var cx = stage.stageWidth/2; var cy = stage.stageHeight/2; x = (pos.x - cx) * invZ + cx; // transform 3D coordinates to 2D y = (pos.y - cy) * invZ + cy; scaleX = size.x * invZ; // calculate size based on distance scaleY = size.y * invZ; rotation += vr; } // else visible = false // front clipping (I want objects in front of the front clipping plane to 'stick' to the camera so commented out this line } public function update() {} // this will be overridden, should be a pure virtual function if flash supported it! } }
and the simple 3D Vector with a few 3D arithmetic (analogous to Papervisions Number3D class).
/*********************************************************************** BASIC 3D VECTOR CLASS v0.8.0 ----------------------------------- Copyright (c) 2008, Memo Akten, www.memo.tv This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ***********************************************************************/ package msa.sprite3d { public class Vector3D { public var x:Number; public var y:Number; public var z:Number; // constructor public function Vector3D(tx:Number = 0, ty:Number = 0, tz:Number = 0) { set(tx, ty, tz); } // set the x,y,z values in one go public function set(tx:Number = 0, ty:Number = 0, tz:Number = 0) { x = tx; y = ty; z = tz; } // subtract vector p2 from p1 public static function sub(p1:Vector3D, p2:Vector3D):Vector3D { return (new Vector3D(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z) ); } // add vectors p1 and p2 public static function add(p1:Vector3D, p2:Vector3D):Vector3D { return (new Vector3D(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z) ); } // multiply vector p with number n public static function mult(p:Vector3D, n:Number):Vector3D { return (new Vector3D(p.x * n, p.y * n, p.z * n) ); } // returns a copy (new instance) of the vector public function clone():Vector3D { return (new Vector3D(x, y, z) ); } } }
| Attachment | Size |
|---|---|
| download source files here | 23.67 KB |
| view finished SWF | 3.02 KB |
Delicious
Digg
StumbleUpon





