Flex:3D Introduction

PS:此篇为转文,原文链接:http://www.cnblogs.com/yjmyzz/archive/2010/05/08/1730697.html

三维坐标系:右手法则坐标系

三维透视基本规则:
物体坐标在Z轴上越大(越远),则看起来越小,如果距离足够远,则物体消失于屏幕上的某个特定点(“消失点”)

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

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    
    public class Base3D extends Sprite
    {
        public function Base3D()
        {
            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,0X000FF,    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    += 50;
                else if (e.keyCode == Keyboard.DOWN)
                    zPos    -= 50;
            }
        }
    }
}
3D View

此例中,"鼠标位置"充当"观察点"(即"人眼"位置),键盘↑↓键可调整小球在Z轴上的位置。移动鼠标,通过辅助线观察变化。

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    
    public class Base3D 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 Base3D()
        {
            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    -= 10;
                    break;
                case Keyboard.DOWN :
                    vy    += 10;
                    break;
                case Keyboard.LEFT :
                    vx    -= 10;
                    break;
                case Keyboard.RIGHT :
                    vx    += 10;
                    break;
                case Keyboard.SHIFT :
                    vz    += 5;
                    break;
                case Keyboard.CONTROL :
                    vz    -= 5;
                    break;
                default :
                    break;
            }
        }
    }
}
3D Motion

此例中,↑↓←→控制x,y轴方向速度,shift/ctrl控制z轴速度

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    
    public class Base3D 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 Base3D()
        {
            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.getRadius();
            
            //左右边界
            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);
        }
    }
}
3D Motion Rebound

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

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    
    public class Base3D 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 Base3D()
        {
            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;
            
            //边界检测
            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);
        }
    }
}
Multi-3D-Motion Rebound

仔细观察会发现:物体前后顺序不对,远处的物体挡住了近处的物体(css中可通过z-Index调整,silverlight的canvas也有类似zIndex,但在As3中如何做呢?)
Flash的显示列表中,最后被addChild的物体总是显示在上面,在Flash内部"舞台上的每个物体"都对应一个索引值,随着物体不断被添加到舞台上,其对应的索引值也不断增加,我们可通过调整索引值来改变物体的显示顺序

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    
    public class Base3D 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 Base3D()
        {
            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();
        }
        
        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);
            }
        }
        
        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;
            }
            
            //转换化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);
        }
    }
}
Ajusted-Multi-3D-Motion Rebound
package
{
    import fl.controls.Label;
    
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    
    //设置动画背景为黑色 
    [SWF(backgroundColor    = 0X000000)]    //c#中的特性
    public class Base3D 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 Base3D()
        {
            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)*2 //x轴方向速度为-3到+3的随机值(即:看起来有的球向左,有的球向右,在横向扩散)
            b.vy    = (Math.random()-1)*2; //y轴方向为-4到0之间的随机值(即向下掉)
            b.vz    = (Math.random()-1)*2;  //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);
            }
        }
    }
}
3D-Particle-Injection

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

package
{
    import fl.controls.Label;
    
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    
    //设置动画背景为黑色 
    [SWF(backgroundColor    = 0X000000)]    //c#中的特性
    public class Base3D extends Sprite
    {
        
        public function Base3D()
        {
            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.x    = Math.random()*2000-1000;
                    tree.y    = floor;
                    tree.z    = 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;
                }   
            }
            
            //按上下键时的速度处理
            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.z    += vz;
                
                //如果物体跑到观察点后面了,则重新将其放在无限远处
                if (tree.z<fl*-1) {
                    tree.z+=10000;
                }
                
                //如果物体跑得太远了,重新将其放在观察点身后
                if (tree.z>10000-fl) {
                    tree.z-=10000;
                }
                
                var scale:Number = fl / (fl + tree.z);
                tree.scaleX=tree.scaleY=scale;
                tree.x=vpX+tree.x*scale;
                tree.y=vpY+tree.y*scale;
                tree.alpha=scale*0.8+0.2;
            }
            
            //z轴排序
            function sortZ():void {
                trees.sortOn("z", Array.DESCENDING | Array.NUMERIC);
                for (var i:uint = 0; i < numTrees; i++) {
                    var tree:Tree=trees[i];
                    setChildIndex(tree, i);
                }
            }
            
            init();
        }
    }
}
Z-Axias Around

 

这个示例中用到了一个新的类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);
        }
    }
}
Tree

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

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;
    
    [SWF(backgroundColor=0x000000)]
    public class Base3D 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 Base3D() {
            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);
            }
        }
    }
}
StrengthedOfAbove

3D缓动:

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    public class Base3D 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 Base3D() 
        {
            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();
        }
        
        private 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轴排序  
        private 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-LowMove

这个示例中,对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();
        }
    }
}
Ball3D-Modified

3D弹性运动:

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    public class Base3D 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;
        
        private var temp:Number = 0;
        
        public function Base3D() {
            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);
        }
        
        private 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轴排序  
        private 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-Motion-Spring

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 Base3D 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 Base3D() {
            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);
            }
        }
    }
}
RotateY

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

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    public class Base3D extends Sprite {
        public function Base3D() {
            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();
        }
    }
}
RotateXYZ

3D碰撞检测:当2球间距离小于二球半径和时,即认为碰撞

package
{
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.ColorTransform;
    
    public class Base3D 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 Base3D() 
        {
            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);
            }
        }
    }
}
Collision3D

 本节用到的Ball类:

package{
    import flash.display.Sprite;
    
    public class Ball extends Sprite{
        
        private var radius:Number;
        private var color:uint;
        private var vx:Number    = 0;    //x轴速度
        private var vy:Number    = 0;    //y轴速度
        
        public function Ball(r:Number=50,c:uint=0XFF0000){
            this.radius = r;
            this.color = c;
            init();
        }
        
        private function init():void
        {
            graphics.beginFill(color);
            graphics.drawCircle(0, 0, radius);
            graphics.endFill();
        }
        
        public function getRadius():Number
        {
            return radius;
        }
    }
}
Ball

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2015-08-04 09:47  cangyu  阅读(203)  评论(0)    收藏  举报