• Flash/Flex学习笔记(49):3D基础


    之前我们所做的动画都是基于x,y二维坐标轴的,在三维动画中我们还需要增加一个垂直于屏幕“向里”或“向外”的Z轴,那么z轴到底是应该向外,还是向里呢?这个其实无所谓,不过为了统一,习惯上通常把z轴约定为向里,即所谓的“右手坐标系”

    右手坐标系的得名:伸出右手,让食指、中指、大拇指相互垂直;然后 食指指向x轴正向,中指指向y轴正向,则大拇指所指方向即为z轴正向。(事实上这个姿势酷似周杰伦周董的招牌动作)

    三维透视的基本规则:

    物体在Z轴上的坐标越大(越远),则看起来越小(将上图坐标系旋转,把z轴转到x轴方向后,得到下图),如果距离足够远,则物体将消失于屏幕上的某个特定点(通常称为“消失点”)

    技术上的主要处理:动态调整物体的scaleX与scaleY(同时因为物体的大小改变后,相应的x,y坐标值通常也会改变,所以x,y坐标也要做相应调整以符合透视规则),基本公式如下:

    scale = fl/(fl+z)

    var ball:Ball = new Ball();
    addChild(ball);
    
    //观察点 相对于 消失点的坐标
    var xPos:Number=0;
    var yPos:Number=0;
    var zPos:Number=0;
    var fl:Number=250;//焦距
    //消失点
    var vpX:Number=stage.stageWidth/2;
    var vpY:Number=stage.stageHeight/2;
    
    addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
    stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
    stage.addEventListener(MouseEvent.MOUSE_WHEEL,MouseWheelHandler);
    
    //鼠标滚轮事件(注:必须让stage获取焦点时-即用鼠标在动画上点击一下,该事件才会触发,另外还要注意:嵌入网页时,浏览器也会响应鼠标滚轮)
    function MouseWheelHandler(e:MouseEvent):void {
    	zPos += (e.delta*5);
    }
    
    function EnterFrameHandler(event:Event):void {
    	if (zPos> -fl) {
    		ball.visible=true;
    		xPos=mouseX-vpX;
    		yPos=mouseY-vpY;
    		var scale:Number = fl / (fl + zPos);
    		ball.scaleX=ball.scaleY=scale;
    		ball.x=vpX+xPos*scale;
    		ball.y=vpY+yPos*scale;
    	} else {
    		ball.visible=false;
    	}
    	
    	//辅助线
    	graphics.clear();
    	graphics.lineStyle(1,0xcccccc);
    	graphics.moveTo(vpX,vpY);	
    	graphics.lineTo(vpX,ball.y);
    	graphics.moveTo(vpX,vpY);
    	graphics.lineTo(ball.x,vpY);
    	
    	
    	graphics.lineStyle(1,0x0000ff,0.5);
    	graphics.moveTo(vpX,vpY);
    	graphics.lineTo(ball.x,ball.y);
    	
    	graphics.lineStyle(1,0xff0000,0.5);
    	graphics.moveTo(ball.x,ball.y);
    	graphics.lineTo(mouseX,mouseY);	
    }
    
    //键盘事件
    function KeyDownHandler(e:KeyboardEvent):void {
    	if (e.keyCode==Keyboard.UP) {
    		zPos+=5;
    	} else if (e.keyCode == Keyboard.DOWN) {
    		zPos-=5;
    	}
    }

    这个示例中,"鼠标所在位置"充当了"观察点"(即前面示意图中的"人眼"位置),电脑屏幕所在平面即物体的成像面,用键盘上下键可调整小球在Z轴上的位置,然后移动鼠标位置,通过辅助线观察变化。

    基本的3D运动:

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.events.KeyboardEvent;
    	import flash.ui.Keyboard;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	
    	public class Velocity3D extends Sprite {
    		private var ball:Ball;
    		
    		//相当于消失点的坐标
    		private var xpos:Number=0;
    		private var ypos:Number=0;
    		private var zpos:Number=0;
    		
    		//x,y,z三轴上的速度分量
    		private var vx:Number=0;
    		private var vy:Number=0;
    		private var vz:Number=0;
    		
    		private var friction:Number=0.98;
    		private var fl:Number=250;
    		
    		//消失点
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    		
    		public function Velocity3D() {
    			init();
    		}
    		private function init():void {
    			stage.align = StageAlign.TOP_LEFT;	
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			ball = new Ball(20);
    			addChild(ball);
    			addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
    			stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
    		}
    		private function EnterFrameHandler(event:Event):void {
    			vpX =stage.stageWidth/2;
    			vpY =stage.stageHeight/2;
    			
    			xpos+=vx;
    			ypos+=vy;
    			zpos+=vz;
    			vx*=friction;
    			vy*=friction;
    			vz*=friction;
    			
    			if (zpos>-fl) {
    				var scale:Number = fl / (fl + zpos);
    				ball.scaleX=ball.scaleY=scale;
    				ball.x=vpX+xpos*scale;
    				ball.y=vpY+ypos*scale;
    				ball.visible=true;
    			} else {
    				ball.visible=false;
    			}
    			
    			//辅助线
    			graphics.clear();
    			graphics.lineStyle(1,0xefefef);
    			graphics.moveTo(0,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth-15,stage.stageHeight/2-8);
    			graphics.moveTo(stage.stageWidth,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth-15,stage.stageHeight/2+8);
    			
    			graphics.moveTo(stage.stageWidth/2,0);
    			graphics.lineTo(stage.stageWidth/2,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth/2-8,stage.stageHeight-15);
    			graphics.moveTo(stage.stageWidth/2,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth/2+8,stage.stageHeight-15);
    			graphics.lineStyle(1,0xdadada);
    			graphics.moveTo(vpX,vpY);
    			graphics.lineTo(ball.x,ball.y);
    			
    			
    		}
    		private function KeyDownHandler(e:KeyboardEvent):void {
    			switch (e.keyCode) {
    				case Keyboard.UP :
    					vy-=1;
    					break;
    				case Keyboard.DOWN :
    					vy+=1;
    					break;
    				case Keyboard.LEFT :
    					vx-=1;
    					break;
    				case Keyboard.RIGHT :
    					vx+=1;
    					break;
    				case Keyboard.SHIFT :
    					vz+=0.5;
    					break;
    				case Keyboard.CONTROL :
    					vz-=0.5;
    					break;
    				default :
    					break;
    			}
    		}
    	}
    }
    

    上下左右键控制x,y轴方向速度,shift/ctrl键控制z轴速度(当然这个示例还没考虑到3D环境中的边界反弹,下面就要谈到这个问题)

    3维运动反弹:

    这个需要一点想象力,二维情况下,通常只要把舞台的四个边当作边界就足够了,但是试想一下:在一个立体的空间里,要限制一个物体的活动范围,得要有6个边界面(上,下,左,右,前,后)

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	
    	public class Bounce3D extends Sprite {
    		private var ball:Ball;
    		private var xpos:Number=0;
    		private var ypos:Number=0;
    		private var zpos:Number=0;
    		private var vx:Number=Math.random()*12-6;
    		private var vy:Number=Math.random()*12-6;
    		private var vz:Number=Math.random()*12-6;
    		private var fl:Number=250;
    		
    		//消失点
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    		
    		//相对于消失点的六个边界面(上,下,左,右,前,后)
    		private var top:Number=-100;
    		private var bottom:Number=100;
    		private var left:Number=-100;
    		private var right:Number=100;
    		private var front:Number=100;
    		private var back:Number=-100;
    		
    		public function Bounce3D() {
    			init();
    		}
    		private function init():void {
    			stage.align = StageAlign.TOP_LEFT;	
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			ball=new Ball(15);
    			addChild(ball);
    			addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
    		}
    		private function EnterFrameHandler(event:Event):void {
    			vpX =stage.stageWidth/2;
    			vpY =stage.stageHeight/2; 
    			
    			xpos+=vx;
    			ypos+=vy;
    			zpos+=vz;
    			var radius:Number=ball.radius;
    			//左右边界
    			if (xpos+radius>right) {
    				xpos=right-radius;
    				vx*=-1;
    			} else if (xpos - radius < left) {
    				xpos=left+radius;
    				vx*=-1;
    			}
    			//上下边界
    			if (ypos+radius>bottom) {				
    				ypos=bottom-radius;
    				vy*=-1;
    			} else if (ypos - radius < top) {
    				ypos=top+radius;
    				vy*=-1;
    			}
    			//前后边界
    			if (zpos+radius>front) {
    				zpos=front-radius;
    				vz*=-1;
    			} else if (zpos - radius < back) {
    				zpos=back+radius;
    				vz*=-1;
    			}
    			
    			//换算成平面二维坐标及缩放比率
    			if (zpos>- fl) {
    				var scale:Number = fl / (fl + zpos);
    				ball.scaleX=ball.scaleY=scale;
    				ball.x=vpX+xpos*scale;
    				ball.y=vpY+ypos*scale;
    				ball.visible=true;
    			} else {
    				ball.visible=false;
    			}
    			
    			//辅助线
    			graphics.clear();
    			graphics.lineStyle(1,0xccccff);
    			graphics.moveTo(0,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth-15,stage.stageHeight/2-8);
    			graphics.moveTo(stage.stageWidth,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth-15,stage.stageHeight/2+8);
    			
    			graphics.moveTo(0,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth,0);
    			graphics.lineTo(stage.stageWidth-15,2);
    			graphics.moveTo(stage.stageWidth,0);
    			graphics.lineTo(stage.stageWidth-6,13);
    			
    			graphics.moveTo(stage.stageWidth/2,0);
    			graphics.lineTo(stage.stageWidth/2,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth/2-8,stage.stageHeight-15);
    			graphics.moveTo(stage.stageWidth/2,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth/2+8,stage.stageHeight-15);
    			graphics.lineStyle(1,0xffccff);
    			graphics.moveTo(vpX,vpY);
    			graphics.lineTo(ball.x,ball.y);
    		}
    	}
    }
    

    也许这样看得并不清楚,再加入更多的小球反弹,可能更容易理解一些,不过为了方便代码处理,先定义一个新的小球类:Ball3D

    package {
    	import flash.display.Sprite;
    	public class Ball3D extends Sprite {
    		public var radius:Number;
    		private var color:uint;
    		public var xpos:Number=0;
    		public var ypos:Number=0;
    		public var zpos:Number=0;
    		public var vx:Number=0;
    		public var vy:Number=0;
    		public var vz:Number=0;
    		public var mass:Number=1;
    		public function Ball3D(radius:Number=40, color:uint=0xff0000) {
    			this.radius=radius;
    			this.color=color;
    			init();
    		}
    		public function init():void {
    			graphics.lineStyle(0);
    			graphics.beginFill(color);
    			graphics.drawCircle(0, 0, radius);
    			graphics.endFill();
    		}
    	}
    }
    

    多个小球的3D反弹:

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    		import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	
    	public class MultiBounce3D extends Sprite {
    		private var balls:Array;
    		private var numBalls:uint=20;
    		private var fl:Number=250;
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    		private var top:Number=-120;
    		private var bottom:Number=120;
    		private var left:Number=-120;
    		private var right:Number=120;
    		private var front:Number=120;
    		private var back:Number=-120;
    		public function MultiBounce3D() {
    			init();
    		}
    		private function init():void {
    			stage.align = StageAlign.TOP_LEFT;	
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			balls = new Array();
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=new Ball3D(15,Math.random()*0xffffff);
    				balls.push(ball);
    				ball.vx=Math.random()*10-5;
    				ball.vy=Math.random()*10-5;
    				ball.vz=Math.random()*10-5;
    				addChild(ball);
    			}
    			addEventListener(Event.ENTER_FRAME, onEnterFrame);
    		}
    		private function onEnterFrame(event:Event):void {
    			vpX =stage.stageWidth/2;
    			vpY =stage.stageHeight/2; 
    			graphics.clear();
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];				
    				move(ball);
    			}
    		}
    		private function move(ball:Ball3D):void {
    			var radius:Number=ball.radius;
    			ball.xpos+=ball.vx;
    			ball.ypos+=ball.vy;
    			ball.zpos+=ball.vz;
    			
    			//6边界检测
    			if (ball.xpos+radius>right) {
    				ball.xpos=right-radius;
    				ball.vx*=-1;
    			} else if (ball.xpos - radius < left) {
    				ball.xpos=left+radius;
    				ball.vx*=-1;
    			}
    			if (ball.ypos+radius>bottom) {
    				ball.ypos=bottom-radius;
    				ball.vy*=-1;
    			} else if (ball.ypos - radius < top) {
    				ball.ypos=top+radius;
    				ball.vy*=-1;
    			}
    			if (ball.zpos+radius>front) {
    				ball.zpos=front-radius;
    				ball.vz*=-1;
    			} else if (ball.zpos - radius < back) {
    				ball.zpos=back+radius;
    				ball.vz*=-1;
    			}
    			
    			//转换化2D坐标
    			if (ball.zpos>- fl) {
    				var scale:Number = fl / (fl + ball.zpos);
    				ball.scaleX=ball.scaleY=scale;
    				ball.x=vpX+ball.xpos*scale;
    				ball.y=vpY+ball.ypos*scale;
    				ball.visible=true;
    			} else {
    				ball.visible=false;
    			}
    			
    			//辅助线			
    			graphics.lineStyle(1,0xccccff);
    			graphics.moveTo(0,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth-15,stage.stageHeight/2-8);
    			graphics.moveTo(stage.stageWidth,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth-15,stage.stageHeight/2+8);
    			
    			graphics.moveTo(0,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth,0);
    			graphics.lineTo(stage.stageWidth-15,2);
    			graphics.moveTo(stage.stageWidth,0);
    			graphics.lineTo(stage.stageWidth-6,13);
    			
    			graphics.moveTo(stage.stageWidth/2,0);
    			graphics.lineTo(stage.stageWidth/2,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth/2-8,stage.stageHeight-15);
    			graphics.moveTo(stage.stageWidth/2,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth/2+8,stage.stageHeight-15);
    			graphics.lineStyle(1,0xff99ff);
    			graphics.moveTo(vpX,vpY);
    			graphics.lineTo(ball.x,ball.y);
    		}
    	}
    }
    

    仔细观察一下,相信不少人会发现问题:物体的前后顺序不对,远处的物体居然挡住了近处的物体。(css中可以通过z-Index来调整,silverlight的canvas中也有类似的zIndex,但在As3中如何做呢?)

    先跑一下题,来看一个小技巧:Object 数组的排序

    var arrTest = [{age:20,name:"a"},{age:50,name:"b"},{age:30,name:"c"}]
    arrTest.sortOn("age",Array.DESCENDING);//按age值倒排
    for(var i:int=0,j=arrTest.length;i<j;i++){
    	trace(arrTest[i].age ,arrTest[i].name);
    }
    

    是不是很好用!

    ok,问题解决了:Flash的显示列表中,最后被addChild的物体总是显示在上面,在Flash内部"舞台上的每个物体"都对应一个索引值,随着物体不断被添加到舞台上,其对应的索引值也不断增加,我们可以通过调整索引值来改变物体的显示顺序.

    基本测试:

    var ballA = new Ball(50);
    ballA.x = stage.stageWidth/2;
    ballA.y = stage.stageHeight/2;
    addChild(ballA);
    
    var ballB = new Ball(45,0x0000ff);
    ballB.x = ballA.x;
    ballB.y = ballA.y + 20;
    addChild(ballB);
    
    btn1.addEventListener(MouseEvent.MOUSE_DOWN,btn1Click);
    
    function btn1Click(e:MouseEvent):void{
    	setChildIndex(ballB,0);
    	setChildIndex(ballA,1);
    }
    
    btn2.addEventListener(MouseEvent.MOUSE_DOWN,btn2Click);
    
    function btn2Click(e:MouseEvent):void{
    	setChildIndex(ballB,1);
    	setChildIndex(ballA,0);
    }
    

    调整后的3D反弹

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    
    	public class MultiBounce3D extends Sprite {
    		private var balls:Array;
    		private var numBalls:uint=20;
    		private var fl:Number=250;
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    		private var top:Number=-120;
    		private var bottom:Number=120;
    		private var left:Number=-120;
    		private var right:Number=120;
    		private var front:Number=120;
    		private var back:Number=-120;
    		public function MultiBounce3D() {
    			init();
    		}
    		private function init():void {
    			stage.align=StageAlign.TOP_LEFT;
    			stage.scaleMode=StageScaleMode.NO_SCALE;
    			balls = new Array();
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=new Ball3D(15,Math.random()*0xffffff);
    				balls.push(ball);
    				ball.vx=Math.random()*10-5;
    				ball.vy=Math.random()*10-5;
    				ball.vz=Math.random()*10-5;
    				addChild(ball);
    			}
    			addEventListener(Event.ENTER_FRAME, onEnterFrame);
    		}
    		private function onEnterFrame(event:Event):void {
    			vpX=stage.stageWidth/2;
    			vpY=stage.stageHeight/2;
    			graphics.clear();
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				move(ball);
    			}
    			sortZ();
    		}
    
    		function sortZ():void {
    			balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				setChildIndex(ball, i);
    			}
    		}
    
    		private function move(ball:Ball3D):void {
    			var radius:Number=ball.radius;
    			ball.xpos+=ball.vx;
    			ball.ypos+=ball.vy;
    			ball.zpos+=ball.vz;
    
    			//6边界检测
    			if (ball.xpos+radius>right) {
    				ball.xpos=right-radius;
    				ball.vx*=-1;
    			} else if (ball.xpos - radius < left) {
    				ball.xpos=left+radius;
    				ball.vx*=-1;
    			}
    			if (ball.ypos+radius>bottom) {
    				ball.ypos=bottom-radius;
    				ball.vy*=-1;
    			} else if (ball.ypos - radius < top) {
    				ball.ypos=top+radius;
    				ball.vy*=-1;
    			}
    			if (ball.zpos+radius>front) {
    				ball.zpos=front-radius;
    				ball.vz*=-1;
    			} else if (ball.zpos - radius < back) {
    				ball.zpos=back+radius;
    				ball.vz*=-1;
    			}
    
    			//转换化2D坐标
    			if (ball.zpos>- fl) {
    				var scale:Number = fl / (fl + ball.zpos);
    				ball.scaleX=ball.scaleY=scale;
    				ball.x=vpX+ball.xpos*scale;
    				ball.y=vpY+ball.ypos*scale;
    				ball.visible=true;
    			} else {
    				ball.visible=false;
    			}
    
    			//辅助线
    			graphics.lineStyle(1,0xccccff);
    			graphics.moveTo(0,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth-15,stage.stageHeight/2-8);
    			graphics.moveTo(stage.stageWidth,stage.stageHeight/2);
    			graphics.lineTo(stage.stageWidth-15,stage.stageHeight/2+8);
    
    			graphics.moveTo(0,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth,0);
    			graphics.lineTo(stage.stageWidth-15,2);
    			graphics.moveTo(stage.stageWidth,0);
    			graphics.lineTo(stage.stageWidth-6,13);
    
    			graphics.moveTo(stage.stageWidth/2,0);
    			graphics.lineTo(stage.stageWidth/2,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth/2-8,stage.stageHeight-15);
    			graphics.moveTo(stage.stageWidth/2,stage.stageHeight);
    			graphics.lineTo(stage.stageWidth/2+8,stage.stageHeight-15);
    			graphics.lineStyle(1,0xff99ff);
    			graphics.moveTo(vpX,vpY);
    			graphics.lineTo(ball.x,ball.y);
    		}
    	}
    }
    

    3D粒子喷射:

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	
    	//设置动画背景为黑色	
    	[SWF(backgroundColor=0x000000)]//c#中的特性? 哈
    	public class Fireworks extends Sprite {
    		private var balls:Array;
    		private var numBalls:uint=100;
    		private var fl:Number=250;
    		//消失点
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    		private var gravity:Number=0.2;
    		private var floor:Number=50;//y轴反弹的边界(相对消失点而言)
    		private var bounce:Number=-0.6;
    		public function Fireworks() {
    			init();
    		}
    
    		private function init():void {
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			stage.align = StageAlign.TOP_LEFT;
    			balls = new Array();
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=new Ball3D(5,Math.random()*0xffffff);
    				balls.push(ball);				
    				addChild(ball);
    			}
    			initVelocity();
    			addEventListener(Event.ENTER_FRAME, onEnterFrame);
    			
    		}
    		
    		private function initVelocity():void{
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				reset(ball);
    			}			
    		}
    		
    		private function reset(b:Ball3D):void{
    			b.ypos=-250;
    			b.zpos=200;		
    			b.xpos=0;
    			b.vx=(Math.random()*2-1)*3 //x轴方向速度为-3到+3的随机值(即:看起来有的球向左,有的球向右,在横向扩散)
    			b.vy=(Math.random()-1)*4; //y轴方向为-4到0之间的随机值(即向下掉)
    			b.vz=(Math.random()-1)*3;  //z轴方向速度为-3到0的随机值(即:所有球从远处向近处喷)
    				
    		}
    		
    		private function onEnterFrame(event:Event):void {			
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				move(ball);
    			}
    			sortZ();
    		}
    		private function move(ball:Ball3D):void {
    			ball.vy+=gravity;
    			ball.xpos+=ball.vx;
    			ball.ypos+=ball.vy;
    			ball.zpos+=ball.vz;
    			if (ball.ypos>floor) {
    				ball.ypos=floor;
    				ball.vy*=bounce;
    			}
    			if (ball.zpos>-fl) {
    				var scale:Number = fl / (fl + ball.zpos);
    				ball.scaleX=ball.scaleY=scale;
    				ball.x=vpX+ball.xpos*scale;
    				ball.y=vpY+ball.ypos*scale;
    				ball.alpha = scale;//越远的物体,越淡
    				if (ball.x<0 || ball.x>stage.stageWidth || ball.y>stage.stageHeight || ball.alpha<0.05){
    					reset(ball);
    				}
    				ball.visible=true;
    			} else {
    				ball.visible=false;
    				reset(ball);
    			}
    		}
    		private function sortZ():void {
    			balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				setChildIndex(ball, i);
    			}
    		}
    	}
    }
    

    Z轴上的屏幕环绕:

    类似2d中的处理,当物体在z轴的坐标达到某一限定值时,重新将其设置为相反值,通俗点讲:物体太远(接近看不见时),重新将其放在无限近的地方(通常是观察者背后),物体太近(甚至跑到观察者背后时),重新将其放在无限远处.

    var trees:Array;
    var numTrees:uint=100;
    var fl:Number=250;
    var vpX:Number=stage.stageWidth/2;
    var vpY:Number=stage.stageHeight/2;
    var floor:Number=50;
    var vz:Number=0;
    var friction:Number=0.98;
    var temp:Number=0;
    
    
    function init():void {
    	trees = new Array();
    	for (var i:uint = 0; i < numTrees; i++) {
    		var tree:Tree = new Tree();
    		trees.push(tree);
    		tree.xpos=Math.random()*2000-1000;
    		tree.ypos=floor;
    		tree.zpos=Math.random()*10000;
    		addChild(tree);
    	}
    	addEventListener(Event.ENTER_FRAME, EnterFrameHandler);
    	stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
    }
    
    function EnterFrameHandler(event:Event):void {
    	for (var i:uint = 0; i < numTrees; i++) {
    		var tree:Tree=trees[i];
    		move(tree);
    	}
    	vz*=friction;
    	sortZ();
    
    	//自动播放的处理
    	if (temp>500) {
    		vz-=0.5;		
    	} else {
    		vz+=0.5;
    	}
    	temp++;	
    	if (temp==1000){
    		temp=0;
    	}	
    	trace(temp);
    }
    
    //按上下键时的速度处理
    function KeyDownHandler(event:KeyboardEvent):void {
    	if (event.keyCode==Keyboard.UP) {
    		vz-=1;
    	} else if (event.keyCode == Keyboard.DOWN) {
    		vz+=1;
    	}
    }
    
    
    function move(tree:Tree):void {
    	tree.zpos+=vz;
    	
    	//如果物体跑到观察点后面了,则重新将其放在无限远处
    	if (tree.zpos<fl*-1) {
    		tree.zpos+=10000;
    	}
    	
    	//如果物体跑得太远了,重新将其放在观察点身后
    	if (tree.zpos>10000-fl) {
    		tree.zpos-=10000;
    	}
    	
    	var scale:Number = fl / (fl + tree.zpos);
    	tree.scaleX=tree.scaleY=scale;
    	tree.x=vpX+tree.xpos*scale;
    	tree.y=vpY+tree.ypos*scale;
    	tree.alpha=scale*0.8+0.2;
    }
    
    //z轴排序
    function sortZ():void {
    	trees.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
    	for (var i:uint = 0; i < numTrees; i++) {
    		var tree:Tree=trees[i];
    		setChildIndex(tree, i);
    	}
    }
    
    init();
    

    这个示例中用到了一个新的类Tree,代码如下:

    package {
    	import flash.display.Sprite;
    	public class Tree extends Sprite {
    		public var xpos:Number=0;
    		public var ypos:Number=0;
    		public var zpos:Number=0;
    		public function Tree() {
    			init();
    		}
    		public function init():void {
    			graphics.lineStyle(0,0xffffff);
    			graphics.lineTo(0,-140-Math.random()*20);
    			graphics.moveTo(0,-30-Math.random()*30);
    			graphics.lineTo(Math.random()*80-40,-100-Math.random()*40);
    			graphics.moveTo(0,-60-Math.random()*40);
    			graphics.lineTo(Math.random()*60-30,-110-Math.random()*20);
    		}
    	}
    }
    

    上面这个示例的加强版:(左右键控制x轴加速度,上下键控制z轴加速度,空格键整体下落)

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.events.KeyboardEvent;
    	import flash.ui.Keyboard;
    	
    	[SWF(backgroundColor=0x000000)]
    	public class Trees2 extends Sprite {
    		private var trees:Array;
    		private var numTrees:uint=100;
    		
    		private var fl:Number=250;
    		
    		//消失点
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    		
    		private var floor:Number=50;
    		
    		
    		//加速度
    		private var ax:Number=0;
    		private var ay:Number=0;
    		private var az:Number=0;
    		
    		//速度
    		private var vx:Number=0;
    		private var vy:Number=0;
    		private var vz:Number=0;
    		
    		//重力与摩擦力
    		private var gravity:Number=0.3;
    		private var friction:Number=0.98;
    		
    		public function Trees2() {
    			init();
    		}
    		
    		private function init():void {
    			trees = new Array();
    			for (var i:uint = 0; i < numTrees; i++) {
    				var tree:Tree = new Tree();
    				trees.push(tree);
    				tree.xpos=Math.random()*2000-1000;
    				tree.ypos=floor;
    				tree.zpos=Math.random()*10000;
    				addChild(tree);
    			}
    			addEventListener(Event.ENTER_FRAME, onEnterFrame);
    			stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDownHandler);
    			stage.addEventListener(KeyboardEvent.KEY_UP, KeyUpHandler);
    		}
    		
    		private function onEnterFrame(event:Event):void {
    			vx+=ax;
    			vy+=ay;
    			vz+=az;
    			vy-=gravity;//负重力?哈,好好理解一下,这一行结合后面的y轴坐标检测,才保证了所有树在空格键松开时,能在y轴方向上恢复原状
    			for (var i:uint = 0; i < numTrees; i++) {
    				var tree:Tree=trees[i];
    				move(tree);
    			}
    			vx*=friction;
    			vy*=friction;
    			vz*=friction;
    			sortZ();
    		}
    		
    		//键盘按下时,处理加速度
    		private function KeyDownHandler(event:KeyboardEvent):void {
    			switch (event.keyCode) {
    				case Keyboard.UP :
    					az=-1;
    					break;
    				case Keyboard.DOWN :
    					az=1;
    					break;
    				case Keyboard.LEFT :
    					ax=1;
    					break;
    				case Keyboard.RIGHT :
    					ax=-1;
    					break;
    				case Keyboard.SPACE :
    					ay=1;
    					break;
    				default :
    					break;
    			}
    		}
    		
    		//按键抬起时,加速度置0
    		private function KeyUpHandler(event:KeyboardEvent):void {
    			switch (event.keyCode) {
    				case Keyboard.UP :
    				case Keyboard.DOWN :
    					az=0;
    					break;
    				case Keyboard.LEFT :
    				case Keyboard.RIGHT :
    					ax=0;
    					break;
    				case Keyboard.SPACE :
    					ay=0;
    					break;
    				default :
    					break;
    			}
    		}
    		
    		
    		private function move(tree:Tree):void {
    			tree.xpos+=vx;
    			tree.ypos+=vy;
    			tree.zpos+=vz;
    			
    			
    			//y轴上的坐标判断,否则所有树将一直向上跑
    			if (tree.ypos<floor) {
    				tree.ypos=floor;
    			}
    			
    			//z轴屏幕环绕
    			if (tree.zpos<fl*-1) {
    				tree.zpos+=10000;
    			}
    			if (tree.zpos>10000-fl) {
    				tree.zpos-=10000;
    			}
    			
    			var scale:Number = fl / (fl + tree.zpos);
    			tree.scaleX=tree.scaleY=scale;
    			tree.x=vpX+tree.xpos*scale;
    			tree.y=vpY+tree.ypos*scale;
    			tree.alpha=scale*0.8 + 0.2;
    		}
    		
    		//z轴排序
    		private function sortZ():void {
    			trees.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
    			for (var i:uint = 0; i < numTrees; i++) {
    				var tree:Tree=trees[i];
    				setChildIndex(tree, i);
    			}
    		}
    	}
    }
    

    3D缓动:

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	public class Easing3D extends Sprite {		
    		private var balls:Array;
    		private var ballNum:Number=20;
    		private var easing:Number=.1;
    		private var fl:Number=250;
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    
    		public function Easing3D() {
    			init();
    		}
    		private function init():void {
    			balls=new Array(ballNum);
    			for (var i:int=0; i<ballNum; i++) {
    				balls[i]=new Ball3D(20,Math.random()*0xffffff);
    				balls[i].tx=(Math.random()*2-1)*200;
    				balls[i].ty=(Math.random()*2-1)*200;
    				balls[i].tz=(Math.random()*2-1)*500;
    				addChild(balls[i]);
    			}
    			addEventListener(Event.ENTER_FRAME, onEnterFrame);
    		}
    		
    		
    		private function onEnterFrame(event:Event):void {
    			for (var i:int=0; i<ballNum; i++) {
    				move(balls[i]);
    			}
    			sortZ();
    		}
    
    		function move(b:Ball3D):void {
    			var dx:Number=b.tx-b.xpos;
    			var dy:Number=b.ty-b.ypos;
    			var dz:Number=b.tz-b.zpos;
    			//缓动公式
    			b.xpos+=dx*easing;
    			b.ypos+=dy*easing;
    			b.zpos+=dz*easing;
    			var dist:Number=Math.sqrt(dx*dx+dy*dy+dz*dz);
    			if (dist<1) {
    				b.tx=(Math.random()*2-1)*200;
    				b.ty=(Math.random()*2-1)*200;
    				b.tz=(Math.random()*2-1)*500;
    			}
    			if (b.zpos > -fl) {
    				var scale:Number = fl / (fl + b.zpos);
    				b.scaleX=b.scaleY=scale;
    				b.x=vpX+b.xpos*scale;
    				b.y=vpY+b.ypos*scale;
    				b.alpha=scale*0.7+0.3;
    				b.visible=true;
    			} else {
    				b.visible=false;
    			}
    		}
    
    		//z轴排序  
    		function sortZ():void {
    			balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
    			for (var i:uint = 0; i < ballNum; i++) {
    				var b:Ball3D=balls[i];
    				setChildIndex(b, i);
    			}
    
    		}
    
    
    	}
    }
    

    当然这个示例中,要对Ball3D做一些改造:

    package {
    	import flash.display.Sprite;
    	public class Ball3D extends Sprite {
    		public var radius:Number;
    		private var color:uint;
    		
    		public var xpos:Number=0;
    		public var ypos:Number=0;
    		public var zpos:Number=0;
    		
    		public var vx:Number=0;
    		public var vy:Number=0;
    		public var vz:Number=0;
    		
    		public var tx:Number=0;
    		public var ty:Number=0;
    		public var tz:Number=0;
    		
    		public var mass:Number=1;
    		
    		public function Ball3D(radius:Number=40, color:uint=0xff0000) {
    			this.radius=radius;
    			this.color=color;
    			init();
    		}
    		public function init():void {
    			graphics.lineStyle(0);
    			graphics.beginFill(color);
    			graphics.drawCircle(0, 0, radius);
    			graphics.endFill();
    		}
    	}
    }
    

    3D弹性运动:

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.events.MouseEvent;
    	public class Spring3D extends Sprite {
    
    		private var balls:Array;
    		private var ballNum:Number=20;
    
    		private var spring:Number=.1;
    		private var friction:Number=.94;
    		private var fl:Number=250;
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    		
    		var temp:Number = 0;
    		
    		public function Spring3D() {
    			init();
    		}
    		private function init():void {
    			balls=new Array(ballNum);
    			for (var i:int=0; i<ballNum; i++) {
    				balls[i]=new Ball3D(20,Math.random()*0xffffff);
    				balls[i].tx=(Math.random()*2-1)*200;
    				balls[i].ty=(Math.random()*2-1)*200;
    				balls[i].tz=(Math.random()*2-1)*300;
    				addChild(balls[i]);
    			}
    			addEventListener(Event.ENTER_FRAME, onEnterFrame);
    			
    		}
    		private function onEnterFrame(event:Event):void {
    			for (var i:int=0; i<ballNum; i++) {
    				move(balls[i]);
    			}
    			sortZ();
    			
    			temp++;
    			if (temp>=250){
    				reset();
    				temp=0;
    			}
    			//trace(temp);
    		}
    
    		function move(b:Ball3D):void {
    			var dx:Number=b.tx-b.xpos;
    			var dy:Number=b.ty-b.ypos;
    			var dz:Number=b.tz-b.zpos;
    			b.vx+=dx*spring;
    			b.vy+=dy*spring;
    			b.vz+=dz*spring;
    			b.xpos+=b.vx;
    			b.ypos+=b.vy;
    			b.zpos+=b.vz;
    			b.vx*=friction;
    			b.vy*=friction;
    			b.vz*=friction;
    			if (b.zpos>- fl) {
    				var scale:Number = fl / (fl + b.zpos);
    				b.scaleX=b.scaleY=scale;
    				b.x=vpX+b.xpos*scale;
    				b.y=vpY+b.ypos*scale;
    				b.alpha=scale*0.8+0.2;
    				b.visible=true;
    			} else {
    				b.visible=false;
    			}
    		}
    
    		private function reset():void {
    			for (var i:int=0; i<ballNum; i++) {
    				balls[i].tx=(Math.random()*2-1)*200;
    				balls[i].ty=(Math.random()*2-1)*200;
    				balls[i].tz=(Math.random()*2-1)*300;
    			}
    		}
    
    		//z轴排序  
    		function sortZ():void {
    			balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
    			for (var i:uint = 0; i < ballNum; i++) {
    				var b:Ball3D=balls[i];
    				setChildIndex(b, i);
    			}
    
    		}
    	}
    }
    

    3D坐标旋转:

    其实跟2D坐标旋转几乎完全相同,只不过要指定绕哪个轴旋转

    绕z轴旋转

    x1 = cos(angleZ) * x - sin(angleZ) * y;
    y1 = cos(angleZ) * y + sin(angleZ) * x;

    绕y轴旋转
    x1 = cos(angleY) * x - sin(angleY) * z;
    z1 = cos(angleY) * z + sin(angleY) * x;

    绕x轴旋转
    y1 = cos(angleX) * y - sin(angleX) * z;
    z1 = cos(angleX) * z + sin(angleX) * y;

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	public class RotateY extends Sprite {
    		private var balls:Array;
    		private var numBalls:uint=50;
    
    		private var fl:Number=250;
    		private var vpX:Number=stage.stageWidth/2;
    		private var vpY:Number=stage.stageHeight/2;
    		public function RotateY() {
    			init();
    		}
    		private function init():void {
    			balls = new Array();
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=new Ball3D(10,Math.random()*0xffffff);
    				balls.push(ball);
    				ball.xpos=Math.random()*200-100;
    				ball.ypos=Math.random()*200-100;
    				ball.zpos=(Math.random()*2-1)*100;
    				addChild(ball);
    			}
    			addEventListener(Event.ENTER_FRAME, onEnterFrame);
    		}
    		private function onEnterFrame(event:Event):void {
    			var angleY:Number = (mouseX - vpX) * .0004;//旋转的角度与鼠标的水平位置关联
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				rotateY(ball, angleY);
    			}
    			sortZ();
    		}
    		
    		//绕y轴旋转
    		private function rotateY(ball:Ball3D, angleY:Number):void {
    			var cosY:Number=Math.cos(angleY);
    			var sinY:Number=Math.sin(angleY);
    			var x1:Number=ball.xpos*cosY-ball.zpos*sinY;
    			var z1:Number=ball.zpos*cosY+ball.xpos*sinY;
    			ball.xpos=x1;
    			ball.zpos=z1;
    			if (ball.zpos>- fl) {
    				var scale:Number = fl / (fl + ball.zpos);
    				ball.scaleX=ball.scaleY=scale;
    				ball.x=vpX+ball.xpos*scale;
    				ball.y=vpY+ball.ypos*scale;
    				ball.alpha = scale*0.8;
    				ball.visible=true;
    			} else {
    				ball.visible=false;
    			}
    		}
    		private function sortZ():void {
    			balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				setChildIndex(ball, i);
    			}
    		}
    	}
    }
    

    这个示例还可以扩充到3个轴的同时旋转:

    var balls:Array;
    var numBalls:uint=50;
    
    var fl:Number=250;
    var vpx:Number=stage.stageWidth/2;
    var vpy:Number=stage.stageHeight/2;
    
    function init():void {
    	balls=new Array(numBalls);
    	for (var i:uint=0; i<numBalls; i++) {
    		var ball:Ball3D=new Ball3D(10,Math.random()*0xffffff);
    		balls[i]=ball;
    		ball.xpos = (Math.random()*2-1)*100;
    		ball.ypos = (Math.random()*2-1)*100;
    		ball.zpos = (Math.random()*2-1)*100;
    		addChild(ball);
    	}
    	addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
    }
    
    function EnterFrameHandler(e:Event):void {	
    	var dx:Number = mouseX - vpx;
    	var dy:Number = mouseY - vpy;	
    	var angleY:Number =dx*0.0005;
    	var angleX:Number =dy*0.0005;
    	var angleZ:Number = Math.sqrt(dx*dx + dy*dy)*0.0005;
    	
    	if (dx>0){angleZ *=-1;}//以鼠标所在点的x坐标相对于消失点的位置为判断依据,左侧z轴正向旋转,右侧z轴反向旋转
    	
    	for (var i:uint; i<numBalls; i++) {
    		var b:Ball3D=balls[i];
    		rotateX(b,angleX);
    		rotateY(b,angleY);
    		rotateZ(b,angleZ);
    		doPerspective(b);
    	}
    	sortZ();
    }
    
    //x轴的坐标旋转
    function rotateX(ball:Ball3D, angleX:Number):void {
    	var cosX:Number=Math.cos(angleX);
    	var sinX:Number=Math.sin(angleX);
    	var y1:Number=ball.ypos*cosX-ball.zpos*sinX;
    	var z1:Number=ball.zpos*cosX+ball.ypos*sinX;
    	ball.ypos=y1;
    	ball.zpos=z1;
    }
    
    //y轴的坐标旋转
    function rotateY(ball:Ball3D, angleY:Number):void {
    	var cosY:Number=Math.cos(angleY);
    	var sinY:Number=Math.sin(angleY);
    	var x1:Number=ball.xpos*cosY-ball.zpos*sinY;
    	var z1:Number=ball.zpos*cosY+ball.xpos*sinY;
    	ball.xpos=x1;
    	ball.zpos=z1;
    }
    
    //z轴的坐标旋转
    function rotateZ(ball:Ball3D, angleZ:Number):void {
    	var cosZ:Number=Math.cos(angleZ);
    	var sinZ:Number=Math.sin(angleZ);
    	var x1:Number=ball.xpos*cosZ-ball.ypos*sinZ;
    	var y1:Number=ball.ypos*cosZ+ball.xpos*sinZ;
    	ball.xpos=x1;
    	ball.ypos=y1;
    }
    
    //3D透视处理
    function doPerspective(ball:Ball3D):void {
    	if (ball.zpos>-fl) {
    		var scale:Number = fl / (fl + ball.zpos);
    		ball.scaleX=ball.scaleY=scale;
    		ball.x=vpx+ball.xpos*scale;
    		ball.y=vpy+ball.ypos*scale;
    		//ball.alpha = scale*0.65;
    		ball.visible=true;
    	} else {
    		ball.visible=false;
    	}
    }
    
    //z轴排序
    function sortZ():void {
    	balls.sortOn("zpos",Array.DESCENDING|Array.NUMERIC);
    	for (var i:uint=0; i<numBalls; i++) {
    		setChildIndex(balls[i],i);
    	}
    }
    
    init();
    

    3D碰撞检测:

    基本原理仍然可以套用前面讲过的“基于距离的检测”,当二个球之间的距离小于二球的半径合时,即认为发生了碰撞,至于碰撞检测出来以后再如何处理,就要发挥想象了

    package {
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.geom.ColorTransform;
    	public class Collision3D extends Sprite {
    		private var balls:Array;
    		private var numBalls:uint=8;
    		private var fl:Number=250;
    		private var vpX:Number=stage.stageWidth/2;
    
    		private var vpY:Number=stage.stageHeight/2;
    		private var top:Number=-100;
    		private var bottom:Number=100;
    		private var left:Number=-100;
    		private var right:Number=100;
    		private var front:Number=100;
    		private var back:Number=-100;
    		public function Collision3D() {
    			init();
    		}
    		private function init():void {
    			balls = new Array();
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=new Ball3D(10);
    				balls.push(ball);
    				ball.xpos=Math.random()*400-200;
    				ball.ypos=Math.random()*400-200;
    				ball.zpos=Math.random()*400-200;
    				ball.vx=Math.random()*10-5;
    				ball.vy=Math.random()*10-5;
    				ball.vz=Math.random()*10-5;
    				addChild(ball);
    			}
    			addEventListener(Event.ENTER_FRAME, onEnterFrame);
    		}
    		private function onEnterFrame(event:Event):void {
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				move(ball);
    			}
    			for (i = 0; i < numBalls - 1; i++) {
    				var ballA:Ball3D=balls[i];
    				for (var j:uint = i + 1; j < numBalls; j++) {
    					var ballB:Ball3D=balls[j];
    					var dx:Number=ballA.xpos-ballB.xpos;
    					var dy:Number=ballA.ypos-ballB.ypos;
    					var dz:Number=ballA.zpos-ballB.zpos;
    					var dist:Number=Math.sqrt(dx*dx+dy*dy+dz*dz);
    					if (dist<ballA.radius+ballB.radius) {
    						var newTransform:ColorTransform =
    						new ColorTransform(0, 1, 1, 1, Math.random()*255, Math.random()*255, Math.random()*255, 0);
    						ballA.transform.colorTransform=newTransform;
    						ballB.transform.colorTransform=newTransform;
    					}
    				}
    			}
    			sortZ();
    		}
    		private function move(ball:Ball3D):void {
    
    			var radius:Number=ball.radius;
    			ball.xpos+=ball.vx;
    			ball.ypos+=ball.vy;
    			ball.zpos+=ball.vz;
    			if (ball.xpos+radius>right) {
    				ball.xpos=right-radius;
    				ball.vx*=-1;
    			} else if (ball.xpos - radius < left) {
    				ball.xpos=left+radius;
    				ball.vx*=-1;
    			}
    			if (ball.ypos+radius>bottom) {
    				ball.ypos=bottom-radius;
    				ball.vy*=-1;
    			} else if (ball.ypos - radius < top) {
    				ball.ypos=top+radius;
    				ball.vy*=-1;
    			}
    			if (ball.zpos+radius>front) {
    				ball.zpos=front-radius;
    				ball.vz*=-1;
    			} else if (ball.zpos - radius < back) {
    				ball.zpos=back+radius;
    				ball.vz*=-1;
    			}
    			if (ball.zpos>- fl) {
    				var scale:Number = fl / (fl + ball.zpos);
    				ball.scaleX=ball.scaleY=scale;
    				ball.x=vpX+ball.xpos*scale;
    				ball.y=vpY+ball.ypos*scale;
    				ball.visible=true;
    			} else {
    				ball.visible=false;
    			}
    		}
    		private function sortZ():void {
    			balls.sortOn("zpos", Array.DESCENDING | Array.NUMERIC);
    			for (var i:uint = 0; i < numBalls; i++) {
    				var ball:Ball3D=balls[i];
    				setChildIndex(ball, i);
    			}
    		}
    	}
    }
    

    上面的示例中,生二球发生碰撞后,颜色变为随机色(当然您可以处理根据前面讲过的动量守恒原理对速度做处理,不过在3D空间中要同时计算x,y,z三个轴的速度大小以及方向,运算量是比较大的,各位有兴趣可自行尝试)

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    4.9 省选模拟赛 圆圈游戏 树形dp set优化建图
    C#异步编程の-------异步编程模型(APM)
    C#异步编程の----Threadpool( 线程池)
    C#の----Func,Action,predicate在WPF中的应用
    C#常见委托のdelegate定义,Func,Action,Predicate总结
    c++のmap的遍历
    C#深度学习の----深拷贝与浅拷贝
    NSIS学习记录の----查找注册表某个键是否存在
    WPFの操作文件浏览框几种方式
    C#Url处理类
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1730697.html
Copyright © 2020-2023  润新知