| 首页 | 新闻 | 网页 | 设计 | 色彩 | 原创 | 视觉 | 素材 | 动漫 | 酷站 | 策划 | 文案 | 访谈 | 运营 | 编程 | 数据库 | 服务器 | 下载 | 图库 | 
您的位置: 幽幽天空 > 网页 > 网页制作 > Flash教程 > ActionScript教程 > 文章正文 用户登录
用Flash和FlashFo
二维、三维矢量类
Particle粒子发射
绘制三维曲线
从球形绳的制作学
利用SWiSHmax实现
Flash 与三维全景
从球形绳的制作学
商业三维动画短片
Flash制作水纹三维

三维粒子系统在AS2中的实现           

三维粒子系统在AS2中的实现

作者:佚名 来源:闪吧 作者: EmilMatthew 更新:2007-1-13 20:36:49 错误报告 我要投稿
  摘要:  
       本文通过粒子系统及三维粒子透视投影变换,运用Flash AS2及其开发环境,实现了基本的三维粒子系统。并结合相关的曲线及运动方式,实现了抛体运动,Fermat 螺线上升运动以及龙卷风效果。表现了一定的数字艺术层的美感。
 
关键词: 粒子系统,三维,数字艺术 
       
1前言:
       三维粒子系统是一类令人感到激动又十分有趣的动画程序。它的实现方式主要需要用基于粒子系统构建的图形学,动力学以及数字艺术等多方面的知识。[1]介绍了基本的三维视图通过透视投影变换到二维场景的方式。[2]介绍了一个基本的粒子系统的实现。在这两篇文章的基础上,再结合比较简单的运动学方面的知识,在本文中实现了基本的三维粒子系统,并实现了抛体运动,Fermat 螺线上升运动以及龙卷风效果。这些作品表现了一定的数字艺术层的美感。 
       另外,开发选用的是基于AS2语言的Flash开发平台,这种开发模式具有以下优点:
1)      Flash 播放器具有极高的普及率,而且swf文件格式是跨平台的。
2)      Flash 的失量图形处理模式极强,适合网络传播图像,动画及各类互动效果。
3)      Flash 平台下的交互动画开发方便,采用了AS2语言可以面向对象的模式来组织程序结构。交互效果可以实现的非常到位。可以说,是一种面像图形开发的脚本语言。(正如Matlab可以说是面向科学工程计算的脚本语言一样。)
 
当然,这种模式也是有缺点的,比如Flash播放器在处理一些较大运算时的效果不是很
理想。
由于这个程序重点在实现相应的图形效果上,主要是测试之用,所以采用Flash 的 AS2
来进行是非常合适的。
 
2程序总体思路及关键部分:
2.1粒子系统的框架:
[2]中已有叙述,这里简要回顾之: 
       Initialization 
  
  
while(runFlag) 
       { 
                     For all particles 
                     { 
                            If(current particle is not lived) 
                            { 
                                          Init this particle. 

                            Else if(current particle is out of the showing area) 
                            { 
                                   Current particle set to dead. 


2.2三维粒子在二维场景的透视成像:
       [1]中已有详述,这里简要回顾之:
       对于一个在三维场景中的点p(x ,y ,z)
       其在距离原点距离为d处的位于z轴正半轴上的点(0,0,d)而言,其对应到二维投影面的点p’ (x’,y’)有如下计算公式: 
              x’=x/(-z/d+1)                 
y’=y/(-z/d+1)          


                                                                         图1                             
 
       2.3.1抛体运动模式关键:
       抛体运动在这里的右手坐标系(图1)中,是指以y为竖直运方向作上抛运动,x , z方向以固定速度运动的方式。我们在公园里看到的许多喷泉就是这样的例子。
       由于初始化后所有粒子统一生成,所以要看到类似喷泉的效果,需要待几个粒子生成周期之后,生成粒子与死亡粒子可以持续交替时,便会呈现出不错的效果。图2展示了300粒子在程序运行过几个粒子生成周期后的效果。
       相应初始程序、参数及运动函数如下:
       //data set 
tmp=Math.random();                                        _root.particleArr[i].initV_A((2+random(3))*Math.cos(2*Math.PI*tmp),15+random(10),(2+random(3))*Math.sin(2*Math.PI*tmp),0,-0.5+0.15*Math.random(),0); 
         _root.particleArr[i].initPos(gX0-10+random(20),gY0-50+random(150),gZ0-10+random(20)); 
                      
       //motion effect mode 
       for(var i:Number=0;i<_root.gParticleNum;i++) 
                     { 
                                   if(_root.particleArr[i].isLived()) 
                                   { 
                                          _root.particleArr[i].moveCal(); 
                                          _root.particleArr[i].moveShow(); 
                                   } 
                     } 
图2     

       2.3.2Fermat 螺线上升运动:
       Fermat螺线的极坐标表达为:
                            ρ=a*θ ^(1/2)
       这里要做的,就是将θ角在某粒子与xz平面垂直的截平面上对应的Fermat螺线极坐标转换成x及z坐标值。再进行相应的更新即可。与前一种不同的是,这里只有y轴方向需要用到vy,而x,z轴的位置则由于采用了坐标控制模式,所以不需要使用vx,vz。为了整体程序设计上的统一,可将vx,vz设定为0。
       与上一个抛物线型运动不同,这里的Fermat螺线运动如果新生粒子与死亡粒子构成了持续生成关系时,将使画面较为混乱。因此,需要将粒子的初始y值及vy,ay设定的靠近些。
       可以看到,团状的粒子簇旋转上升后又扩散下降,无序之中体现着一丝韵律,我将这样的效果命名为“银河落九天”,不知诸位为以为如何?(参图3) 
  
     //data set 
tmp=Math.random(); 
       _root.particleArr[i].initV_A(0,18+random(2),0,0,-0.5+0.1*Math.random(),0); 
                                    
       _root.particleArr[i].initRotateEle(0,0,2*Math.PI*i/_root.gParticleNum); 
              _root.particleArr[i].initPos(gX0-15+random(30),gY0-5+random(10),gZ0-15+random(30)); 
                      
       _root.particleArr[i].fermatSpiralA=15+random(10); 
  
       //motion effect mode 
       for(var i:Number=0;i<_root.gParticleNum;i++) 
                     { 
                                   if(_root.particleArr[i].isLived()) 
                                   { 
                                           
                                          _root.particleArr[i].accTheta(Math.PI/18); 
                                           
                                          _root.particleArr[i].moveCalFermatSpiralWithY(); 
                                          _root.particleArr[i].moveShow(); 
                                   } 
                     } 
图3 
 

      2.3.3龙卷风效果:
       龙卷风效果,这里没有采用从坐标原点生成的模式,而是直接摸拟了生成后运动的效果,难度降低了不小。
       可以这样近似看待龙卷风的模拟,每个粒子都以y轴为中心,以不同的运动半径作圆周运动。各粒子的运动半径从下往上递增。
       以上面的设计理念,便可生成一种倒锥型的龙卷风,如图4。显然,在这种模式中,没有粒子会死亡。
//data set  
_root.particleArr[i].initV_A((0.5*Math.random())*Math.cos(2*Math.PI*tmp),0,(0.5*Math.random())*Math.sin(2*Math.PI*tmp),0,0,0); 
                                                                      _root.particleArr[i].initRotateEle(0,0,2*Math.PI*Math.random()); 
                                                  
_root.particleArr[i].initPos(gX0+(i+1)*2-2+random(4),gY0+50-baconHeight/_root.gParticleNum*i-random(50),gZ0+(i+1)*2-2+random(4)); 
                      
baconHeight=400 
//motion effect mode  
for(var i:Number=0;i<_root.gParticleNum;i++) 
                     { 
                                   if(_root.particleArr[i].isLived()) 
                                   { 
                                          _root.particleArr[i].accTheta(Math.PI/18); 
                                           
                                          _root.particleArr[i].moveWithY(); 
                                                         
                                          _root.particleArr[i].moveShow(); 
                                   } 
                     } 
图4 

       如果再对粒子生成加以改进,即位于龙卷风高度的75%以下的部分主要生成一个类似柱状的效果,而对于龙卷风高度的75%以上部分则扩散出去,形成一个更为逼真的效果。(如图5)
       1控制生成高度及速度: 
if(i<0.7*_root.gParticleNum) 
       { 
       _root.particleArr[i].initV_A((5*Math.random())*Math.cos(2*Math.PI*tmp),0,(5*Math.random())*Math.sin(2*Math.PI*tmp),0,0,0);                     
                            _root.particleArr[i].initPos(gX0-50+random(100),gY0+50-baconHeight/_root.gParticleNum*i-random(50),gZ0-50+random(100)); 
       } 
       else 
       {            _root.particleArr[i].initV_A((5*Math.random())*Math.cos(2*Math.PI*tmp),0,(5*Math.random())*Math.sin(2*Math.PI*tmp),0,0,0);                     
       if(random(100)<50)                            _root.particleArr[i].initPos(gX0+100+random(100),gY0+25-baconHeight/_root.gParticleNum*i-random(25),gZ0+100+random(100)); 
       else                       _root.particleArr[i].initPos(gX0-100-random(100),gY0+25-baconHeight/_root.gParticleNum*i-random(25),gZ0-100-random(100)); 
       } 
2保持上下不同的旋转速度: 
       if(i<0.7*_root.particleNum) 
              _root.particleArr[i].accTheta(Math.PI/24+0.05*Math.random()); 
       else 
              _root.particleArr[i].accTheta(Math.PI/18+0.1*Math.random()); 
    图5(1000粒) 
 

3关键代码:
3.1粒子类 
import E3DPack.*; 
import SColDet.*; 
import SMotion.*; 
class E3DPack.E3DPhyNode extends E3DNode 

              //--location properties extends.-- 
              /* 
              public var x:Number; 
              public var y:Number; 
              public var z:Number; 
              */ 
               
              public var bufferX:Number=0;//for the clear,re-draw mode here. 
              public var bufferY:Number=0; 
              public var bufferZ:Number=0; 
               
              //--dynamic properties-- 
               
              public var m:Number=0;//mass 
        
              public var g:Number=0;//gravity 
        
              public var pF:Number=0;//Positive forces,attention here UpCase!!!!!!! 
              //Because the compiler was not so perfect as you think ,add a p here to prepare for the case. 
              public var r:Number =0;//when it become a ball---radius. 
        
              public var v :Number=0;//1 demision Velocity or together Velocity of vx ,vy 
              public var vx:Number=0; 
              public var vy:Number=0; 
              public var vz:Number=0; 
        
              public var f :Number=0;//fraction forces. 
              public var fx:Number=0; 
              public var fy:Number=0; 
              public var fz:Number=0; 
        
              public var a :Number=0;//acclerate v 
              public var ax:Number=0; 
              public var ay:Number=0; 
              public var az:Number=0; 
               
              public var fN:Number=0; 
              public var aN:Number=0; 
              public var theta:Number=0;           
              public var aNXZ:Number=0;         //the accleration value for rotate with y-axis 
              public var rXZ:Number=0;           //the radius in rotate with y axis. 
               
               
              //for fermat spirals only 
              var fermatSpiralA:Number=5; 
               
              //--life properties of particles-- 
              private static var DEAD:Number=0; 
              private static var LIVED:Number=1; 
              private var life:Number; 
        
              private static var thisP:Object; 
               
              //--ulti operation component-- 
              private var mMotionCom:RCSMove; 
              private var mColDetCom:RCSColDet; 
               
              //--function which is useful by extends.-- 
              /* 
              public function E3DNode(inX:Number,inY:Number,inZ:Number) 
              public function resetXYZ(inX:Number,inY:Number,inZ:Number) 
              public function getPerspective(viewDistance:Number):Number 
              public function transTo2DNode(viewDistance:Number):E2DNode 
              public function transTo2DNode2(projectPos:Number):E2DNode 
              public function rotateAroundZ(fi:Number):Void 
              public function rotateAroundX(fi:Number):Void 
              public function rotateAroundY(fi:Number):Void                                                                                                                                                       */ 
               
               
              //--self function-- 
              public function E3DPhyNode(inX:Number,inY:Number,inZ:Number) 
              { 
                            x=inX; 
                            y=inY; 
                            z=inZ; 
              } 
               
              public function setLife(lifeValue:Number):Void 
              { 
                     life=lifeValue; 
              } 
        
              public function getLife():Number 
              { 
                     return life; 
              } 
        
              public function isLived():Boolean 
              { 
                     return life==LIVED; 
              } 
               
              //-initialization functions- 
              public function initThisPtr():Void 
              { 
                            thisP=this; 
              }             
               
              public function initCom():Void 
              { 
                     mMotionCom=new RCSMove(); 
                     mColDetCom=new RCSColDet(); 
              } 
               
  
public function initV_A(inVx:Number,inVy:Number,inVz:Number,inAx:Number,inAy:Number,inAz:Number):Void 
              {      
                     this.vx=inVx; 
                     this.vy=inVy; 
                     this.vz=inVz; 
                      
                     this.ax=inAx; 
                     this.ay=inAy; 
                     this.az=inAz; 
              } 
        
              public function initPos(inX:Number,inY:Number,inZ:Number):Void 
              { 
                     this.x=inX; 
                     this.y=inY; 
                     this.z=inZ; 
                      
                     this.bufferX=this.x; 
                     this.bufferY=this.y; 
                     this.bufferZ=this.z; 
              } 
               
              public function initRotateEle(inFn:Number,inAn:Number,inTheta:Number):Void 
              { 
                     this.fN=inFn; 
                     this.aN=inAn; 
                     this.theta=inTheta; 
              } 
               
              //--move and show functions-- 
              public function moveCal():Void 
              { 
                     this.vx+=this.ax; 
                     this.vy+=this.ay; 
                     this.vz+=this.az; 
                      
                     this.bufferX+=this.vx; 
                     this.bufferY-=this.vy; 
                     this.bufferZ+=this.vz; 
              } 
               
              public function moveShow():Void 
              { 
                     var tmp2DNode=new E2DNode(0,0); 
               
                     /*global varialbe depends: 
                     gNodeColor,gNodeTransparent,gOffsetX,gOffsetY 
                                                                                                         */ 
                                                                                                          
                     //1.clear the old node 
                     //in a uplever , global clear mode. 
               
                     //2.update the position    ,actually ,there is no need to use bufferx,y,z here in flash. 
                     //but the design here seem to be more flexible 
                     this.x=this.bufferX; 
                     this.y=this.bufferY; 
                     this.z=this.bufferZ; 
                      
                     //3.redraw.       
                     tmp2DNode=this.transTo2DNode2(_root.gZProjectDis); 
                      
                     _root.gBrush.moveTo(_root.gOffsetX+Number(tmp2DNode.x)-1,_root.gOffsetY+Number(tmp2DNode.y)+1); 
                             
                     _root.gBrush.lineTo(_root.gOffsetX+Number(tmp2DNode.x)+1,_root.gOffsetY+Number(tmp2DNode.y)-1);               
               
              } 
               
              //--check functions-- 
               
              public function outDetect():Boolean 
              { 
               
                     if(this.y>400) 
                            return true; 
                     else 
                            return false; 
              } 
               
              //--other ulti moving functions-- 
              public function accTheta(detaAng:Number):Void 
              { 
                     this.theta+=detaAng; 
              } 
               
              public function calANXZ():Void 
              { 
                     this.aNXZ=(this.vx*this.vx+this.vz*this.vz)/Math.sqrt(this.x*this.x+this.z*this.z);    
              } 
                      
              public function calRXZ():Void 
              { 
                     this.rXZ=Math.sqrt(this.x*this.x+this.z*this.z); 
              } 
                      
              public function calAxAzInRotate():Void 
              { 
                     this.aN=(this.vx*this.vx+this.vz*this.vz)/Math.sqrt(this.x*this.x+this.z*this.z); 
                      
                     this.ax=this.aN*Math.cos(this.theta); 
                     this.az=this.aN*Math.sin(this.theta); 
              } 
               
              public function calAxAzInRotate2():Void 
              { 
                     this.ax=this.aNXZ*Math.cos(this.theta); 
       &nb,sp;             this.az=this.aNXZ*Math.sin(this.theta); 
              } 
               
              public function moveWithY():Void 
              { 
                     this.bufferX=this.rXZ*Math.cos(this.theta); 
                     this.bufferZ=this.rXZ*Math.sin(this.theta); 
              } 
               
              public function moveCalFermatSpiralWithY():Void 
              { 
                     this.vy+=this.ay; 
               
                     this.bufferX=this.fermatSpiralA*Math.sqrt(this.theta)*Math.cos(this.theta); 
                      
                     this.bufferY-=this.vy; 
                      
                     this.bufferZ=this.fermatSpiralA*Math.sqrt(this.theta)*Math.sin(this.theta); 
              } 
         3.2初始化函数(以抛物线运动作为示例) 
function initParticles():Void 

       for(var i:Number=0;i<_root.gParticleNum;i++) 
       { 
                     var tmp:Number=0; 
                     var tmpVr:Number=0; 
               _root.particleArr[i].setLife(LIVED); 
               _root.particleArr[i].initThisPtr(); 
               _root.particleArr[i].initCom(); 
               
                      tmp=Math.random(); 
                      
                     //_root.initRotateEle(2,2,0); 
                     _root.particleArr[i].initV_A((2+random(3))*Math.cos(2*Math.PI*tmp),15+random(10),(2+random(3))*Math.sin(2*Math.PI*tmp),0,-0.5+0.15*Math.random(),0); 
                     _root.particleArr[i].initPos(gX0-10+random(20),gY0-50+random(150),gZ0-10+random(20)); 
                      
       } 
        

  
       3.3主循环函数(以抛物线运动为基础) 
function mainLoop():Void 

       var tmp:Number=0; 
        
       if(_root.gRunFlag)//a infinite looping system. 
       { 
                      
                     //--live check or regenerate particle--  
                     for(var i:Number=0;i<_root.gParticleNum;i++) 
                     { 
                       if(!_root.particleArr[i].isLived())               
                                                 { 
                                                  
                                                         _root.particleArr[i].setLife(LIVED); 
                                                         
                                                          tmp=Math.random(); 
                                                              _root.particleArr[i].initV_A((2+random(3))*Math.cos(2*Math.PI*tmp),15+random(10),(2+random(3))*Math.sin(2*Math.PI*tmp),0,-0.5+0.15*Math.random(),0); 
                                                              _root.particleArr[i].initPos(gX0-10+random(20),gY0+random(50),gZ0-10+random(20)); 
                                                 } 
                            else if(_root.particleArr[i].outDetect()) 
                            { 
                                                 _root.particleArr[i].setLife(DEAD); 
                            } 
                     } 
                      
                     //--particles updating and redraw.-- 
                      
                     //--last pic clear.     -- 
                     _root.gBrush.clear(); 
                      
                     //--new brush set   -- 
                     _root.gBrush.lineStyle(1,_root.gNodeColor,_root.gNodeTransparent); 
                      
                     for(var i:Number=0;i<_root.gParticleNum;i++) 
                     { 
                                   if(_root.particleArr[i].isLived()) 
                                   { 
                                           
                                          _root.particleArr[i].moveCal(); 
                                          _root.particleArr[i].moveShow(); 
                                   } 
                     } 
       } 
文章录入:skyuu    责任编辑:skyuu 
  • 上一篇文章:

  • 下一篇文章:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
    网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
    发表评论:
    姓名:  评 分: 1分 2分 3分 4分 5分
     
  • 严禁发表危害国家安全、政治、黄色淫秽等内容的评论。
  • 用户需对自己在使用幽幽天空服务过程中的行为承担法律责任。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表机友个人观点,与本网站立场无关。