Flash与3D编程探秘(三)- 摄像机(Camera)
?
在前面的两节中,你,作为观察者,所在空间的位置是一成不变的,物体在来回移动,让你产生了3D错觉。但是随着对3D的深入,会发现只让物体运动并不足够。当讨论3D空间的时候,摄像机理论上代表3D中的一个点,我们从这个点去观看这个空间。为什么要使用摄像机呢?因为观察者希望能够透过一个镜头看到其他所有舞台上的物体,对于他来说,它只需转动眼球就可以看到大千世界的另一面。(其实程序里摄像机只不过是一组数值来表示你的摄像机在3D空间的参数,比如位置)想象一下你在广阔的撒哈拉沙漠里越野,又或者你的朋友小P走向在你的身旁。你的朋友小P慢慢走向你并最终到达离你很近的位置,几下来你们可以交谈了。但是,如果小P站在原地不动,而是你走向他的身旁,那么对于地面来说,小P是不动的,而你(摄像机)是移动的。在这里你的眼睛就充当了摄像机。看看下面的两个动画文件,再对比一下。左边是小P走向你,右边是你走向小P。 移动物体和移动摄像机 ? 你会发现对于你的眼睛来说,你可以并不走动,只要把小P和周围一切的物体都移动到你的面前,也会达到同样的效果。但是哪一种可行呢?下面的两个动画说明了如何在3D空间中使用摄像机。动画效果如下,左面的是移动小P,右边是移动摄像机。
??????
对比移动小P和移动你的摄像机 ? 制作步骤:1. 和以前一样,定义原点,设置焦距,创建舞台。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?origin?is?the?center?of?the?view?point?in?3d?space // ?everything?scale?around?this?point // ?these?lines?of?code?will?shift?3d?space?origin?to?the?center var?origin? = ? new ?Object(); origin.x? = ?stage.stageWidth / 2 ; origin.y? = ?stage.stageHeight / 2 - 70 ; // ?focal?length?of?viewer's?camera var?focal_length? = ? 400 ; ? 2. 下面定义一个摄像机物体,它具有3D空间的x,y,z,并且给它一个移动方向和初始的在z方向的移动速度。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?setup?camera var?camera? = ? new ?Object(); camera.x? = ? 0 ; camera.y? = ? 0 ; camera.z? = ? 0 ; camera.direction? = ? 1 ; camera.speed_z? = ? 6 ; ? 3. 创建一个小球在舞台上。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?create?a?sphere // ?go?to?library,?right?click?on?Sphere,?choose?linkage // ?and?check?Export?for?Actionscript for ?(var?i? = ? 0 ;?i? < ? 1 ;?i ++ ) { ????var?sphere? = ? new ?Sphere(); ????sphere.x_3d? = ? - 30 ; ????sphere.y_3d? = ? 80 ; ????sphere.z_3d? = ? 600 ; ???? // ?add?all?the?spherees?to?the?scene?object ????scene.addChild(sphere); } ? 4. 接下来开始写运动的循环函数。每一次执行函数一开始我们要把摄像机的位置在z方向移动一定的量。如果摄像机移动的离小球很近了的话,让摄像机向反方向移动。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?move?the?spherees?back?and?forth function?run(e:Event) { ???? // ?here?we?offset?the?camera?position?by?its?moving?speed?times?the?direction ????camera.z? += ?camera.speed_z * camera.direction; ???? ???? if ?(camera.z? > ? 600 )??????????????????? // ?if?the?camera?is?too?close?to?the?ball ????{ ????????camera.z? = ? 600 ; ????????camera.direction? = ? - 1 ;?????????? // ?move?camera?backward ????} ???? else ? if ?(camera.z? < ? 0 )??????????????? // ?if?the?camera?is?too?close?to?the?screen ????{ ????????camera.z? = ? 0 ; ????????camera.direction? = ? 1 ; ????} ???? // ?loop?through?all?the?objects?on?the?scene ???? for ?(var?i? = ? 0 ;?i? < ?scene.numChildren;?i ++ ) ????{ ???????? // ?calculate?the?scale?what?the?object?should?be ????????var?scale? = ?focal_length / (focal_length + scene.getChildAt(i).z_3d - camera.z); ????????scene.getChildAt(i).x? = ?(scene.getChildAt(i).x_3d - camera.x) * scale; ????????scene.getChildAt(i).y? = ?(scene.getChildAt(i).y_3d - camera.x) * scale; ???????? // ?properly?scale?the?object?to?look?3d ????????scene.getChildAt(i).scaleX? = ?scene.getChildAt(i).scaleY? = ?scale; ????} } ? 5. 计算出小球离摄像机的x距离,y距离和z距离,然后得出小球的缩放比率。最后把小球缩放并移动到相应的位置。That's it! 不要忘记添加循环函数执行事件。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> this .addEventListener(Event.ENTER_FRAME,?run); ? 注意:你需要考虑让摄像机的z的指不能小于-1乘焦距,如果z小于这个值,那么公式scale = focal_length/(focal_length+z)得出的缩放比率会是负数,那么物体就会开始向后运动。 一个简单的赛车小游戏制作下面运用摄像机的概念来制作一个简单的赛车小游戏,游戏里你可以使用WASD键控制赛车,COOL!那么开始。 简单赛车游戏,键盘WASD控制 ? 1. 定义原点,设置焦距,创建舞台。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?origin?is?the?center?of?the?view?point?in?3d?space // ?everything?scale?around?this?point // ?these?lines?of?code?will?shift?3d?space?origin?to?the?center var?origin? = ? new ?Object(); origin.x? = ?stage.stageWidth / 2 ; origin.y? = ?stage.stageHeight / 2 ; // ?now?create?a?scene?object?to?hold?all?the?spheres var?scene? = ? new ?Sprite(); this .addChild(scene); scene.x? = ?origin.x; scene.y? = ?origin.y; // ?focal?length?of?viewer's?camera var?focal_length? = ? 400 ; ? 2. 下面定义一个摄像机物体,它具有3D空间的x,y,z,并且给它初始的在z方向的移动速度。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?setup?camera var?camera? = ? new ?Object(); camera.x? = ? 0 ; camera.y? = ? - 40 ;?????????????? ? ? // ?make?the?camera?off?the?ground?a?little?bit camera.z? = ? 0 ; camera.speed_z? = ? 0 ;???????????? // ?your?driving?speed ? 3. 创建两个个场景,一个用来盛放所有的赛车,另外一个盛放放有的路边轮胎。然后把它们添加到舞台上。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> var?tires? = ? new ?Sprite();?????????? ? ?? // ?this?sprite?holds?all?the?tires var?cars? = ? new ?Sprite();??????????????? // ?this?sprite?holds?all?the?cars var?txt_speed? = ? new ?TextField();???? // ?your?dashboard txt_speed.x? = ? 20 ; txt_speed.y? = ? 20 ; // ?now?add?them?to?the?screen scene.addChild(tires); scene.addChild(cars); this .addChild(txt_speed); ? 4. 定义一些赛车的运动状态的变量。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?now?here?are?the?variables?determine?the?car's?moving?state var?move_left? = ? false ; var?move_right? = ? false ; var?speed_up? = ? false ; var?brake? = ? false ; ? 5. 那么接下来创建40个轮胎并且把前20个放在路的左边,后20个放在路的右边,给赛道画出一个轮廓。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?now?create?40?tires,?20?on?the?left?and?10?on?the?right for ?(var?i? = ? 0 ;?i? < ? 40 ;?i ++ ) { ????var?tire? = ? new ?Tire(); ???? if ?(i? < ? 20 ) ????{ ????????tire.x_3d? = ? - 400 ; ????????tire.z_3d? = ?i * 500 ; ????} ???? else ????{ ????????tire.x_3d? = ? 400 ; ????????tire.z_3d? = ?(i - 20 ) * 500 ; ????} ????tire.y_3d? = ? 40 ; ????tires.addChild(tire); } ? 6. 创建8个赛车,给它们相应的xyz位置(注意要赛车放在赛道上,设置它们的x范围在-230到230之间)不同的起始z位置和速度,最后添加到舞台上。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?create?8?opponent?cars for ?(var?j? = ? 0 ;?j? < ? 8 ;?j ++ ) { ????var?car? = ? new ?Car(); ????car.x_3d? = ?Math.random() * ( - 230 - 230 ) + 230 ;???????? // ?give?them?random?x?position ????car.y_3d? = ? - 30 ; ????car.z_3d? = ? - 800 + ( 8 - j) * 400 ;????????????????????????????? // ?give?them?speed ????car.speed_z? = ?( 8 - j) * 15 ; ????cars.addChild(car); } ? 7. 接下来要写一个函数,每一次执行这个函数,首先把赛车在z方向移动一定量(赛车相对地面是运动的),然后计算比率,把赛车移动到相应的位置并且缩放。我把它命名updateCar,还是运用摄像机的理移动的基本知识,在每一个摄像机移动后,分别计算出摄像机与小车的xyz距离,然后把小车缩放和移动。注意小车如果离摄像机太远或者被甩到摄像机的后面的话,让它不在屏幕上显示。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?for?each?of?the?running?cycle,?these?two?functions?are?called function?updateCar(car) { ????var?x? = ?car.x_3d - camera.x;???????????? // ?calculate?the?x?distance?between?your?camera?and?car ????var?y? = ?car.y_3d - camera.y;???????????? // ?same?we?can?y?distance ????var?z? = ?car.z_3d - camera.z;??????????? // ?and?z?distance ???? ???? if ?(z? < ? 0 ? || ?z? > ? 10000 )?????????????????? // ?if?car?is?too?far?or?left?behind ????????car.visible? = ? false ;????????????????? ? // ?then?do?not?draw?it ???? else ????????car.visible? = ? true ; ???????? ????car.z_3d? += ?car.speed_z;???????????? // ?move?the?car ????z? = ?car.z_3d - camera.z;???????????????? // ?recaculate?the?z?distance ???? ????var?scale? = ?focal_length / (focal_length + z);???? // ?caculate?the?scale?what?the?car?should?be ????car.x? = ?x * scale; ????car.y? = ?y * scale; ????car.scaleX? = ?car.scaleY? = ?scale;???? // ?scale?it?to?a?proper?size } ? 8. 轮胎的更新函数updateTire和赛车的更新函数类似,不同的是,轮胎相对地面是静止的,所以这里不改变它们的xyz值。如果轮胎已经到了摄像机后面(轮胎的z小于摄像机的z),把这个轮胎重新定位到摄像机前非常远的地方。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> function?updateTire(tire) { ????var?x? = ?tire.x_3d - camera.x; ????var?y? = ?tire.y_3d - camera.y; ????var?z? = ?tire.z_3d - camera.z; ???? ???? if ?(z? < ? 0 ) ????{ ????????tire.z_3d? += ? 10000 ;??????????????? // ?if?the?tire?is?left?behind,?then?offset?it? ????????z? = ?tire.z_3d - camera.z;???????????? ????} ???? ????var?scale? = ?focal_length / (focal_length + z); ????tire.x? = ?x * scale; ????tire.y? = ?y * scale; ????tire.scaleX? = ?tire.scaleY? = ?scale; } ? 9. 下一步,run函数执行首先把摄像头沿z方向移动,然后调用前面写的updateCar和updateTire函数,刷新所有赛车和轮胎的位置和大小。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?here?is?the?function?loop function?run(e:Event) { ????camera.z? += ?camera.speed_z;???????????????????????????? // ?first?move?your?camera ???? ???? for ?(var?i? = ? 0 ;?i? < ?cars.numChildren;?i ++ )?????????? ?? // ?update?all?the?cars ????{ ????????updateCar(cars.getChildAt(i)); ????} ???? for ?(var?j? = ? 0 ;?j? < ?tires.numChildren;?j ++ )???????????? // ?and?the?tires?on?the?side ????{ ????????updateTire(tires.getChildAt(j)); ????} ???? ????txt_speed.text? = ? int (camera.speed_z)? + ? " ?MPH " ;?? // ?show?your?speed ????txt_speed.setTextFormat( new ?TextFormat( " Verdana " ,? 16 ,? 0x444444 ,? true )); } ? 10. 下面是键盘响应事件函数,我写了一些的注释在程序里,不过我相信你应该很快就能看懂,就不打算详细解说了。实现的功能是当按下左键你的赛车左移;按下上键,赛车开始加速(当然极速是需要你来定义的了)等等。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?keyboard?functions function?key_down(e:KeyboardEvent): void { ???? if ?(e.keyCode? == ? 37 )???????????? // ?left?key ????????move_left? = ? true ; ???? if ?(e.keyCode? == ? 39 )???????????? // ?right?key ????????move_right? = ? true ; ???? if ?(e.keyCode? == ? 38 )???????????? // ?up?key ????????speed_up? = ? true ; ???? if ?(e.keyCode? == ? 40 )???????????? // ?down?key ????????brake? = ? true ; } function?key_up(e:KeyboardEvent): void { ???? if ?(e.keyCode? == ? 37 ) ????????move_left? = ? false ; ???? if ?(e.keyCode? == ? 39 ) ????????move_right? = ? false ; ???? if ?(e.keyCode? == ? 38 ) ????????speed_up? = ? false ; ???? if ?(e.keyCode? == ? 40 ) ????????brake? = ? false ; } function?keyboard_response(e:Event): void { ???? if ?(move_left)???????????? ????{ ???????? // ?move?the?camera?to?the?left,?remember?here?the?fast?you?go,?the?fast?your?steer ????????camera.x? -= ?camera.speed_z / 6 ; ???????? if ?(camera.x? < ? - 300 )?camera.x? = ? - 300 ;?? ? // ?limit?your?car?so?it?won't?go?off?the?road ????} ???? if ?(move_right) ????{ ????????camera.x? += ?camera.speed_z / 6 ; ???????? if ?(camera.x? > ? 300 )?camera.x? = ? 300 ;???????? // ?limit?your?car?so?it?won't?go?off?the?road ????} ???? if ?(speed_up) ????{ ????????camera.speed_z? += ?. 2 ;?????????????????? ? ? ? // ?accelerate ???????? // ?limit?the?car?speed?in?a?range ???????? if ?(camera.speed_z? < ? 0 )?camera.speed_z? = ? 0 ;???????????? ???????? else ? if ?(camera.speed_z? > ? 120 )?camera.speed_z? = ? 120 ; ????} ???? else ????{ ????????camera.speed_z? *= ?. 99 ;?????????????????? ?? // ?if?you?don't?hit?the?throttle,?it?will?stop?soon ????} ???? if ?(brake) ????{ ????????camera.speed_z? -= ?. 3 ;?????????????????????? // ?slow?down ???????? if ?(camera.speed_z? < ? 0 )?camera.speed_z? = ? 0 ; ????} } ? 11. 最后,添加循环函数执行和键盘响应事件。如果没问题的话,现在发布运行。成功了!
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> // ?now?initialize?all?the?necessary?event?listeners?and?we?are?done this .addEventListener(Event.ENTER_FRAME,?run); this .addEventListener(Event.ENTER_FRAME,?keyboard_response); stage.addEventListener(KeyboardEvent.KEY_DOWN,?key_down); stage.addEventListener(KeyboardEvent.KEY_UP,?key_up); stage.stageFocusRect? = ? false ; stage.focus? = ?scene; ? 注意:当你在循环一个数组中所有对象时,你可能会遇到想要删除一个对象的情况(可能你需要把这个对象从这个数组删除,然后添加到另外一个数组中),那么在这个删除的过程中你要非常的小心,因为数组在你执行删除操作后的长度会改变,那么你如果循环使用数组长度作为循环次数的话,会造成跳过删除某个对象的现象。 一种解决办法就是在循环之前定义一个变量然后再执行for循环。
<!--
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> var?length? = ?objects.numChildren; ? 目前为止,一直在讨论3D场景的设置,不要担心,从第七篇开始会讲到如何使用代码实现3D物体的绘制,不过我想在那之前我们还是多看几个3D场景的例子加深你的印象。那么,请不要失去耐心,最终你所关心的内容这里一定会有介绍的。 ? ? 关于摄像机的焦距:程序中对摄像机初始化时,使用了一个变量focal_length来对摄像机的镜头进行设置。这个焦距你可以简单理解为物体扭曲的比率,现实中,摄像时镜头焦距越大,那么拍出来的物体的空间扭曲就越小,反而物体在3D空间里的扭曲就越大,程序中也是一样。 ?在这篇或者接下来的文章,我只会使用简单的一个变量focal_length来代表摄像机镜头的设置(当然现实中摄像机的镜头操作要复杂的多,文章中不再涉及,你可以自己添加镜头设置变量及操作) 。下面的动画里,你可以调节摄像机的镜头的焦距来观看物体空间扭曲的程度。 调节摄像机镜头焦距
上一篇?????????
目录?????????
下一篇
非常抱歉,文中暂时不提供源文件下载,如果你需要源文件,请来信或者留言给我。笔者利用工作之余写这些文章,付出了很多汗水,希望读者和转载者能够尊重作者的劳动。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |