布朗运动:
代码语言:javascript复制varnumDots:uint=50;
varfriction:Number=0.9;
vardots:Array;
varlife:uint=0;
functioninit(){
graphics.lineStyle(0,0xffffff,.5);
dots=newArray();
for(vari:uint=0;i<numDots;i ){
vardot:Ball=newBall(2,0x00ff00);
dot.x=Math.random()*stage.stageWidth;
dot.y=Math.random()*stage.stageHeight;
dot.vx=0;
dot.vy=0;
dots.push(dot);
addChild(dot);
checkBound(dot);
}
addEventListener(Event.ENTER_FRAME,enterFrameHandler);
}
//检查边界
functioncheckBound(b:Ball){
if(b.x<b.width/2){
b.x=b.width/2;
}
elseif(b.x>stage.stageWidth-b.width/2){
b.x=stage.stageWidth-b.width/2;
}
if(b.y<b.height/2){
b.y=b.height/2;
}
elseif(b.y>stage.stageHeight-b.height/2){
b.y=stage.stageHeight-b.height/2;
}
}
functionenterFrameHandler(e:Event):void{
//trace(life);
if(life>=200){
graphics.clear();
graphics.lineStyle(0,0xffffff,.5);
life=0;
}
for(vari:uint=0;i<numDots;i ){
vardot:Ball=dots[i];
graphics.moveTo(dot.x,dot.y);
dot.vx =Math.random()-0.5;
dot.vy =Math.random()-0.5;
dot.x =dot.vx;
dot.y =dot.vy;
dot.vx*=friction;
dot.vy*=friction;
checkBound(dot);
graphics.lineTo(dot.x,dot.y);
}
life ;
}
init();
矩形分布:
代码语言:javascript复制vardotNumber:uint=500;
vardots:Array;
varcenterX:uint=stage.stageWidth/2;
varcenterY:uint=stage.stageHeight/2;
varlimitX:uint=50;
varlimitY:uint=100;
functioninit():void{
dots=newArray();
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=newBall(3*Math.random(),0x00ff00);
dot.x=centerX (Math.random()*2-1)*limitX;
dot.y=centerY (Math.random()*2-1)*limitY;
addChild(dot);
dots.push(dot);
}
addEventListener(Event.ENTER_FRAME,enterframeHandler);
}
functionenterframeHandler(e:Event):void{
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=dots[i];
dot.x=centerX (Math.random()*2-1)*limitX;
dot.y=centerY (Math.random()*2-1)*limitY;
/*varix:Number=dot.x;
variy:Number=dot.y;
dot.y=ix;
dot.x=iy*/
}
}
init();
圆形随机分布:
代码语言:javascript复制vardotNumber:uint=500;
vardots:Array;
varcenterX:uint=stage.stageWidth/2;
varcenterY:uint=stage.stageHeight/2;
varradius:uint=75;
functioninit():void{
dots=newArray();
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=newBall(3*Math.random(),0x00ff00);
varangle:Number=2*Math.random()*Math.PI;
varr:Number=Math.random()*radius;
dot.x=centerX r*Math.cos(angle);
dot.y=centerY r*Math.sin(angle);
addChild(dot);
dots.push(dot);
}
addEventListener(Event.ENTER_FRAME,enterframeHandler);
}
functionenterframeHandler(e:Event):void{
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=dots[i];
varangle:Number=2*Math.random()*Math.PI;
varr:Number=Math.random()*radius;
dot.x=centerX r*Math.cos(angle);
dot.y=centerY r*Math.sin(angle);
}
}
init();
更均匀的圆形随机分布:
代码语言:javascript复制vardotNumber:uint=200;
varcenterX:uint=stage.stageWidth/2;
varcenterY:uint=100;
varradius:uint=50;
functioninit():void{
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=newBall(2,0x00ff00);
varangle:Number=2*Math.random()*Math.PI;
varr:Number=Math.random()*radius;
dot.x=centerX r*Math.cos(angle);
dot.y=centerY r*Math.sin(angle);
addChild(dot);
}
//更均匀的随机分布
for(i=0;i<dotNumber;i ){
vardot1:Ball=newBall(2,0x00ff00);
varangle1:Number=2*Math.random()*Math.PI;
varr1:Number=Math.sqrt(Math.random())*radius;//关键在这里,对Math.random()取平方根后,分布变得更均匀了
dot1.x=centerX r1*Math.cos(angle1);
dot1.y=centerY 200 r1*Math.sin(angle1);
addChild(dot1);
}
}
init();
偏向分布:(即在指定的区域内,中心位置分布最密集,离中心越远,分布越稀疏)
代码语言:javascript复制vardotNumber:uint=600;
varcenterX:uint=stage.stageWidth/2;
varmaxWidth:uint=75;
varballs:Array=newArray();
functioninit():void{
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=newBall(2,0x00ff00);
//在y轴方向上随便取二个值,然后计算平均值做为y坐标
vary1=stage.stageHeight*Math.random();
vary2=stage.stageHeight*Math.random();
varty=(y1 y2)/2;
//x轴做类似的处理
varx1=centerX (Math.random()*2-1)*maxWidth;
varx2=centerX (Math.random()*2-1)*maxWidth;
vartx=(x1 x2)/2;
dot.x=tx;
dot.y=ty;
addChild(dot);
balls.push(dot);
}
stage.frameRate=1;
addEventListener(Event.ENTER_FRAME,enterFrameHandler);
}
init();
functionenterFrameHandler(e:Event){
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=balls[i];
vary1=stage.stageHeight*Math.random();
vary2=stage.stageHeight*Math.random();
varty=(y1 y2)/2;
varx1=centerX (Math.random()*2-1)*maxWidth;
varx2=centerX (Math.random()*2-1)*maxWidth;
vartx=(x1 x2)/2;
dot.x=tx;
dot.y=ty;
}
}
多次迭代的偏向分布(类似星云分布)
代码语言:javascript复制vardotNumber:uint=100;
variterations:uint=6;
varballs:Array=newArray();
functioninit():void{
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=newBall(2,0x00ff00);
varxpos:Number=0;
varypos:Number=0;
for(varj:uint=0;j<iterations;j ){
xpos =stage.stageWidth*Math.random();
ypos =stage.stageHeight*Math.random();
}
dot.x=xpos/iterations;
dot.y=ypos/iterations;
addChild(dot);
balls.push(dot);
}
stage.frameRate=1;
addEventListener(Event.ENTER_FRAME,enterFrameHandler);
}
init();
functionenterFrameHandler(e:Event){
for(vari:uint=0;i<dotNumber;i ){
vardot:Ball=balls[i];
varxpos:Number=0;
varypos:Number=0;
for(varj:uint=0;j<iterations;j ){
xpos =stage.stageWidth*Math.random();
ypos =stage.stageHeight*Math.random();
}
dot.x=xpos/iterations;
dot.y=ypos/iterations;
}
}
Timer类的重绘设置:
代码语言:javascript复制varball:Ball=newBall();
varvx:Number=5;
vartimer=newTimer(20);
stage.frameRate=1;//设置flash动画的帧数为1帧/秒
ball.y=stage.stageHeight/2;
ball.vx=5;
addChild(ball);
timer.addEventListener(TimerEvent.TIMER,TimerHandler);
functionTimerHandler(e:TimerEvent):void{
ball.x =ball.vx;
if(ball.x>stage.stageWidth ball.width/2){
ball.x=-ball.width/2;
}
e.updateAfterEvent();//事件触发后,重绘整个stage(建议大家去掉这一行,再看看效果)
}
timer.start();
注意:timer类的计时并不象c#中那样精确,因为跟帧速有关联。
基于时间的动画:
Flash动画是基于帧的(即每进入一帧时,舞台上的对象才会重绘,并触发Enter_Frame事件),这跟Silverlight是基于时间的设计完全不同。一般情况下,这也不是什么问题,但是这样会在不同配置的机器上可能产生不一致的播放效果,比如“一个简单的小球从左运动到右”的简单动画,如果在ENTER_FRAME事件中,用ball.x =ball.vx来处理,在老爷机上,可能swf动画只能达到每秒10帧的播放速度,而在4核的高配置机器上,能达到每秒100帧的播放速度。 问题就来了:假如ball.vx为5,则在老爷机上,小球最终每秒移动了5*10=50像素,而在高配置机器上,小球每秒移动了5*100=500像素,这与开发者期望的并不一样,下面的代码演示了如何制作基于时间的动画,最终让小球在不同配置的机器上运动速度达到一致。(注:在这一点上,不得不承认Silverlight的设计要优于Flash)
代码语言:javascript复制varball:Ball=newBall();
varvx:Number=5;
stage.frameRate=100;//通常在基于时间的动画中,帧数可以设置得高一点(尽管机器最终可能达不到这个帧数.)
ball.y=stage.stageHeight/2;
ball.vx=10;
addChild(ball);
vartimer=getTimer();
addEventListener(Event.ENTER_FRAME,enterFrameHandler);
functionenterFrameHandler(e:Event):void{
varelapsed:Number=getTimer()-timer;//计算每帧之间间隔的时间差(以毫秒为单位)
ball.x =(ball.vx*elapsed/1000);//将毫秒换算成秒,再乘“速度”,最终的效果即:如果帧数低,动画播放得太慢,则一次多移动一些距离;反之则少移动一些距离,起到了动态调整的目的.
if(ball.x>stage.stageWidth ball.width/2){
ball.x=-ball.width/2;
}
timer=getTimer();
}
大家可以尝试把上面的帧数设置,改成200或50,然后再测试下播放效果,会发现小球的移动速度是一致的,不受帧数的影响。(但帧数建议不要低于10,因为人眼的视觉暂留极限大概是0.1秒,低于这个值动画看起来会很卡)
另外,这里对比给出Silverlight的对比代码:
代码语言:javascript复制usingSystem;
usingSystem.Windows;
usingSystem.Windows.Controls;
usingSystem.Windows.Interop;
usingSystem.Windows.Threading;
namespaceSilverlightApplication1
{
publicpartialclassMainPage:UserControl
{
DispatcherTimertmr;
Ballb;
publicMainPage()
{
InitializeComponent();
this.Loaded =newRoutedEventHandler(MainPage_Loaded);
}
voidMainPage_Loaded(objectsender,RoutedEventArgse)
{
Settingssettings=Application.Current.Host.Settings;
settings.EnableFrameRateCounter=true;
settings.MaxFrameRate=1;
b=newBall();//ball是一个自定义控件,里面就一个圆
c.Children.Add(b);
b.SetValue(Canvas.LeftProperty,c.Width/2);
b.SetValue(Canvas.TopProperty,c.Height/2);
tmr=newDispatcherTimer();
tmr.Interval=newTimeSpan(0,0,0,0,20);
tmr.Tick =newEventHandler(tmr_Tick);
tmr.Start();
}
voidtmr_Tick(objectsender,EventArgse)
{
double_left=(double)b.GetValue(Canvas.LeftProperty);
b.SetValue(Canvas.LeftProperty,_left 5);
}
}
}
相同质量的小球碰撞:
Flash/Flex学习笔记(43):动量守恒与能量守恒 里,我们学习了如何用AS3.0来模拟小球的运量守恒,但计算也是很复杂的,对于相同质量的碰撞,其实可以实现得更简单一些。基本原理是,两个物体沿着碰撞的线路交换它们的速度(想深究的同学们,可以自己去解方程验证)。这样我们在处理这种特殊情况时,就可以简化一部分计算,完整代码如下:(注意加★的部分)
代码语言:javascript复制package{
importflash.display.Sprite;
importflash.events.Event;
importflash.geom.Point;
publicclassSameMassextendsSprite{
privatevarballs:Array;
privatevarnumBalls:uint=8;
privatevarbounce:Number=-1.0;
publicfunctionSameMass(){
init();
}
privatefunctioninit():void{
balls=newArray();
for(vari:uint=0;i<numBalls;i ){
//varradius:Number=Math.random()*40 10;
varradius:Number=20;//★★★★★把所有质量强制为相同
varball:Ball=newBall(radius,Math.random()*0xffffff);
ball.mass=radius;
ball.x=i*100;
ball.y=i*50;
ball.vx=Math.random()*10-5;
ball.vy=Math.random()*10-5;
addChild(ball);
balls.push(ball);
}
addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
privatefunctiononEnterFrame(event:Event):void{
for(vari:uint=0;i<numBalls;i ){
varball:Ball=balls[i];
ball.x =ball.vx;
ball.y =ball.vy;
checkWalls(ball);
}
for(i=0;i<numBalls-1;i ){
varballA:Ball=balls[i];
for(varj:Number=i 1;j<numBalls;j ){
varballB:Ball=balls[j];
checkCollision(ballA,ballB);
}
}
}
//舞台边界检测
functioncheckWalls(b:Ball){
if(b.x<b.radius){
b.x=b.radius;
b.vx*=bounce;
}
elseif(b.x>stage.stageWidth-b.radius){
b.x=stage.stageWidth-b.radius;
b.vx*=bounce;
}
if(b.y<b.radius){
b.y=b.radius;
b.vy*=bounce;
}
elseif(b.y>stage.stageHeight-b.radius){
b.y=stage.stageHeight-b.radius;
b.vy*=bounce;
}
}
privatefunctionrotate(x:Number,y:Number,sin:Number,cos:Number,reverse:Boolean):Point{
varresult:Point=newPoint();
if(reverse){
result.x=x*cos y*sin;
result.y=y*cos-x*sin;
}
else{
result.x=x*cos-y*sin;
result.y=y*cos x*sin;
}
returnresult;
}
privatefunctioncheckCollision(ball0:Ball,ball1:Ball):void{
vardx:Number=ball1.x-ball0.x;
vardy:Number=ball1.y-ball0.y;
vardist:Number=Math.sqrt(dx*dx dy*dy);
if(dist<ball0.radius ball1.radius){
//计算角度和正余弦值
varangle:Number=Math.atan2(dy,dx);
varsin:Number=Math.sin(angle);
varcos:Number=Math.cos(angle);
//旋转ball0的位置
varpos0:Point=newPoint(0,0);
//旋转ball1的速度
varpos1:Point=rotate(dx,dy,sin,cos,true);
//旋转ball0的速度
varvel0:Point=rotate(ball0.vx,ball0.vy,sin,cos,true);
//旋转ball1的速度
varvel1:Point=rotate(ball1.vx,ball1.vy,sin,cos,true);
/*//碰撞的作用力
varvxTotal:Number=vel0.x-vel1.x;
vel0.x=((ball0.mass-ball1.mass)*vel0.x 2*ball1.mass*vel1.x)/(ball0.mass ball1.mass);
vel1.x=vxTotal vel0.x;*/
//★★★★★改成速度交换★★★★★
vartemp:Point=vel0;
vel0=vel1;
vel1=temp;
//更新位置
varabsV:Number=Math.abs(vel0.x) Math.abs(vel1.x);
varoverlap:Number=(ball0.radius ball1.radius)-Math.abs(pos0.x-pos1.x);
pos0.x =vel0.x/absV*overlap;
pos1.x =vel1.x/absV*overlap;
//将位置旋转回来
varpos0F:Object=rotate(pos0.x,pos0.y,sin,cos,false);
varpos1F:Object=rotate(pos1.x,pos1.y,sin,cos,false);
//将位置调整为屏幕的实际位置
ball1.x=ball0.x pos1F.x;
ball1.y=ball0.y pos1F.y;
ball0.x=ball0.x pos0F.x;
ball0.y=ball0.y pos0F.y;
//将速度旋转回来
varvel0F:Object=rotate(vel0.x,vel0.y,sin,cos,false);
varvel1F:Object=rotate(vel1.x,vel1.y,sin,cos,false);
ball0.vx=vel0F.x;
ball0.vy=vel0F.y;
ball1.vx=vel1F.x;
ball1.vy=vel1F.y;
}
}
}
}
声音的使用:
声音的使用其实没什么特别的,跟图片,视频等其它资源都差不多.
如上图,在导入一个声音时,可以指定一个类名,然后在代码中,就可以new一个该类的实例了。除此之外,还可以直接加载远程声音,完整代码如下:
代码语言:javascript复制varbgMusic=newSound(newURLRequest("http://210.51.38.234/music/sophie_zelmani_Going_Home.mp3"));
varstf:SoundTransform=newSoundTransform();
stf.volume=0.3;
bgMusic.play(0,0,stf);
varbing:Bing=newBing();
varball:Ball=newBall(30);
ball.vx=5;
ball.vy=5;
ball.x=stage.stageWidth/2;
ball.y=stage.stageHeight/2;
addChild(ball);
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
functionEnterFrameHandler(e:Event):void{
ball.x =ball.vx;
ball.y =ball.vy;
if(ball.x>=stage.stageWidth-ball.radius){
ball.x=stage.stageWidth-ball.radius;
ball.vx*=-1;
bing.play();
}
elseif(ball.x<=ball.radius){
ball.x=ball.radius;
ball.vx*=-1;
bing.play();
}
if(ball.y>=stage.stageHeight-ball.radius){
ball.y=stage.stageHeight-ball.radius;
ball.vy*=-1;
bing.play();
}
elseif(ball.y<=ball.radius){
ball.y=ball.radius;
ball.vy*=-1;
bing.play();
}
}
AnimationinActionScript3.0/MakingThingsMove!一书终于全部啃完了,感谢作者“KeithPeters”大师写出这么好的书,感谢[FL基理文]历时4个月的用心翻译!强烈推荐给想研究Silverlight/Flash动画的朋友们,里面的很多思想和处理方法都是动画编程通用的,并不局限于某一种特定的语言!“师傅领进门,修行在各人”,以后在动画编程的道路上能走多远,就只能看自己的造化了。