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

简介: 布朗运动: varnumDots:uint=50; varfriction:Number=0.9; vardots:Array; varlife:uint=0; functioninit(){ graphics.

布朗运动:

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;
			}
		}
	}
}

声音的使用:

声音的使用其实没什么特别的,跟图片,视频等其它资源都差不多.

img_05b4f3075da63248cbce6d7aba98b6c2.jpg

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

目录
相关文章
|
索引 容器 内存技术
Flash/Flex学习笔记(52):使用TweenLite
TweenLite是第三方出品的专用于各种缓动动画的类库,其性能据说已经超过了Adobe官方的Tween. 从网上找到了一篇中文的说明文档:http://files.cnblogs.com/yjmyzz/tweenLite%e4%b8%ad%e6%96%87%e6%89%8b%e5%86%8c%e4%b8%8e%e5%8f%82%e6%95%b0%e8%af%b4%e6%98%8e.
946 0
|
内存技术
Flash/Flex学习笔记(56):矩阵变换
先回顾一下Silvelright中的矩阵变换[转]WPF中的MatrixTransform,简单点讲:矩阵变换能改变对象的x,y坐标,x或y方向上的缩放,以及对象在x,y轴上的旋转(扭曲变形) 上面这个是WPF/Silverlight中的3*3变换矩阵,其中X,Y用于改变对象的坐标;M11,M22用于对象在x,y轴上的缩放;而M12,M21用于y轴,x轴上的扭曲。
869 0
|
Java 索引 内存技术
Flash/Flex学习笔记(49):3D基础
之前我们所做的动画都是基于x,y二维坐标轴的,在三维动画中我们还需要增加一个垂直于屏幕“向里”或“向外”的Z轴,那么z轴到底是应该向外,还是向里呢?这个其实无所谓,不过为了统一,习惯上通常把z轴约定为向里,即所谓的“右手坐标系” 右手坐标系的得名:伸出右手,让食指、中指、大拇指相互垂直;然后 食指指向x轴正向,中指指向y轴正向,则大拇指所指方向即为z轴正向。
840 0
|
内存技术
Flash/Flex学习笔记(47):反向运动学(上)
先回顾上篇所说的"正向运动学":以人行走的例子来说,基本上可以理解为大腿驱动小腿,小腿驱动脚,从而引发的一系列姿态调整和运动。再举一个例子,我们用着拿一根软鞭或链条的一端挥舞,被手挥舞的这一端会把"能量"向另一端传递(即固定端驱动紧接的部分,而紧接的部分又驱动下一段紧接的部分),从而使整个系统也随之运动.
730 0
|
内存技术
Flash/Flex学习笔记(48):反向运动学(下)
先要复习一下三角函数与余弦定理: 对于直角三角形,三边长a,b,c与三个角A,B,C的关系如下: 正弦函数: 余弦函数: 正切函数: 反正切函数:(好象现在的教科书里改叫“余切”函数)   或 勾股定律:   但对于不是直角的三角形,就必须用余弦定律来处理了...
943 0
|
内存技术 数据格式 XML
Flash/Flex学习笔记(37):不用系统组件(纯AS3)的视频播放器--只有8.82K
以前为了赶项目,利用系统组件制作过一款视频播放器(见Flash/Flex学习笔记(6):制作基于xml数据源的flv视频播放器),但是系统组件实在是太大了,最终生成的swf居然有103K,随着AS3的深入学习,昨天又弄了一个只用AS3的播放器,最终只有8.82K,呵呵,这肥减得那是相当厉害。
1228 0