Flash/Flex学习笔记(57):实用技巧

布朗运动:

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();

矩形分布:

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();

圆形随机分布:

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();

更均匀的圆形随机分布:

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();

偏向分布:(即在指定的区域内,中心位置分布最密集,离中心越远,分布越稀疏)

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;


	}
}

多次迭代的偏向分布(类似星云分布)

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类的重绘设置:

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)

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的对比代码:

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来模拟小球的运量守恒,但计算也是很复杂的,对于相同质量的碰撞,其实可以实现得更简单一些。基本原理是,两个物体沿着碰撞的线路交换它们的速度(想深究的同学们,可以自己去解方程验证)。这样我们在处理这种特殊情况时,就可以简化一部分计算,完整代码如下:(注意加★的部分)

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一个该类的实例了。除此之外,还可以直接加载远程声音,完整代码如下:

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动画的朋友们,里面的很多思想和处理方法都是动画编程通用的,并不局限于某一种特定的语言!“师傅领进门,修行在各人”,以后在动画编程的道路上能走多远,就只能看自己的造化了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏烙馅饼喽的技术分享

看到XNA的弹幕,于是也用SilverLight弄了个弹幕

      这两天在首页看到太多悲观的东西了,给大家来个有趣点的乐呵乐呵,改变下心情       SilverLight的确是好东西,我把我们公司项目中的地图...

382130
来自专栏逍遥剑客的游戏开发

一些VR渲染优化方法

25580
来自专栏IMWeb前端团队

这个问题你应该很熟悉,然后懵逼,然后放弃

今天早晨决定写这篇博文,但是晚上回家的时候,突然一道闪电从脑海劈过,于是临时决定将这个熟悉然后到懵逼最后到放弃的问题分为两部分。第一部分为抛出问题,诚邀各路英雄...

18300
来自专栏HenCoder

HenCoder Android 自定义 View 1-6:属性动画(上手篇)

如果你没听说过 HenCoder,可以先看看这个: HenCoder:给高级 Android 工程师的进阶手册

15420
来自专栏HT

透过WebGL 3D看动画Easing函数本质

50年前的这个月诞生了BASIC这门计算机语言,回想起自己喜欢上图形界面这行,还得归功于当年在win98下用QBASIC照葫芦画瓢敲了一段绘制奥运五环的代码,当...

222100
来自专栏hightopo

透过HT for Web 3D看动画Easing函数本质

11730
来自专栏数据小魔方

PPT手绘利器——布尔运算

今天给大家介绍PPT里一个隐藏的强大功能——布尔运算! 布尔运算 ▽ 别被名字吓着了 其实就是形状与形状之间的交、并、补运算 高中时数学课上都学过集合中的交集、...

341110
来自专栏HT

纯Shading Language绘制飞机火焰效果

上篇《纯Shading Language绘制HTML5时钟》体现了GLSL可编程性特点,但没有体现GLSL可编程出各种酷炫效果的特点,今天我们将用纯Shadin...

23860
来自专栏hightopo

纯Shading Language绘制飞机火焰效果

23360
来自专栏落影的专栏

OpenGL ES实践教程(六)全景视频获取焦点

教程 OpenGL ES实践教程1-Demo01-AVPlayer OpenGL ES实践教程2-Demo02-摄像头采集数据和渲染 OpenGL ES实践...

32650

扫码关注云+社区

领取腾讯云代金券