[Flash]Flash3D编程探秘三

作者:Yang Zhou
感谢:Crystal

介绍

对学习Flash中的3D编程感兴趣?You come to the right place!在文章中,我将陆续的介绍在Flash中使用Actionsript进行3D编程一些理论和实例。这是一篇初级到中级难度的学习资料,如果你 具有一些基本的数学和几何知识,那对你来说不会太难。虽然例子中运用了非常简单的程序构架和实现方法,但是我还是期望你已经有大量的 Actionscirpt和Flash经验,这样你在使用以及理解Actionscript语言的时候不会有太大的障碍。如果3D这个课题对你来说是比较 新的东西,或者你在阅读中发现很多地方不清楚,那么我建议你参考我写过的另外几篇3D的基础知识的文章。

文章中含有大量的代码和一些算法的实现,并且附带源文件。你会发现我写的代码里有大量的注释,如果有什么不明白的话你可以参考一下注释。你可以完全拷贝我写的代码去使用,但是请务必注明出处。

 

点此下载本节源文件

 

在前面的两节中,你在空间的位置是一成不变的。我们一直是让舞台上的物体来回移动,并没有用到摄像机这个概念,在有些时候我们只需要让物体运动。但是随着你对3D的深入,你会发现那些并不足够

 

当我们讨论3D空间的时候,摄像机理论上代表3D中的一个点,我们从这个点去观看这个空间。为什么要使用摄像机呢?因为我们希望能够透过一个镜头看 到其他所有舞台上的物体,其实程序里摄像机只不过是一组数值来表示你的摄像机在3D空间的参数(比如位置)。想象一下你在广阔的撒哈拉沙漠里越野,又或者 你的朋友小P走向在你的身旁。你的朋友小P慢慢走向你并最终到达离你很近的位置,几下来你们可以交谈了。但是,如果小P站在原地不动,而是你走向他的身 旁,那么对于地面来说,小P是不动的,而你(摄像机)是移动的。在这里你的眼睛就充当了我们的摄像机。看看下面的两个动画文件,再对比一下。左边是小P走 向你,右边是你走向小P。

移动物体和移动摄像机

 

你会发现对于你的眼睛来说,你可以并不走动,只要把小P和周围一切的物体都移动到你的面前,也会达到同样的效果。但是哪一种可行呢?下面我们做两个动画来说明如何在我们的3D使用摄像机。动画效果如下,左面的是移动我们的小P,右边是移动我们的摄像机。

对比移动小P和移动你的摄像机

 

 

制作步骤:

1. 和以前一样,定义原点,设置焦距,创建舞台。

// 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/270;
// focal length of viewer's camera
var focal_length = 400;

2. 下面我们定义一个摄像机物体,它具有3D空间的x,y,z,并且我们给它一个移动方向和初始的在z方向的移动速度。

// setup camera
var camera = new Object();
camera.x 
= 0;
camera.y 
= 0;
camera.z 
= 0;
camera.direction 
= 1;
camera.speed_z 
= 6;

3. 创建一个小球在舞台上。

// 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方向移动一定的量。如果摄像机离小球很近了的话,那么让摄像机回来。

// 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_3dcamera.z);
        scene.getChildAt(i).x 
= (scene.getChildAt(i).x_3dcamera.x)*scale;
        scene.getChildAt(i).y 
= (scene.getChildAt(i).y_3dcamera.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! 不要忘记添加循环函数执行事件。

this.addEventListener(Event.ENTER_FRAME, run);

注意:

你需要考虑让摄像机的z的指不能小于-1乘焦距,如果z小于这个值,那么公式scale = focal_length/(focal_length+z)得出的缩放比率会是负数,那么物体就会开始向后运动。

一个简单的赛车小游戏制作

下面我们运用摄像机的概念来制作一个简单的赛车小游戏,游戏里你可以使用上下左右键控制小车,COOL。那么我们开始。

简单赛车游戏,键盘上下左右方向键控制

 

1. 定义原点,设置焦距,创建舞台。

// 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方向的移动速度。

// 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. 创建两个个场景,一个用来盛放所有的小车,另外一个盛放放有的路边轮胎。然后把它们添加到舞台上。

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. 定义一些小车的运动状态的变量。

// 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个放在路的右边,这样我们给赛道画出一个轮廓。

// 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 
= (i20)*500;
    }
    tire.y_3d 
= 40;
    tires.addChild(tire);
}

6. 创建8个小车,给它们相应的xyz位置(注意要赛车放在赛道上,设置它们的x范围在-230到230之间),给他们不同的起始z位置和速度,最后把它们添加到舞台上。

// create 8 opponent cars
for (var j = 0; j < 8; j++)
{
    var car 
= new Car();
    car.x_3d 
= Math.random()*(230230)+230;        // give them random x position
    car.y_3d = 30;
    car.z_3d 
= 800+(8j)*400;                              // give them speed
    car.speed_z = (8j)*15;
    cars.addChild(car);
}

7. 接下来我们要写一个函数,每一次我们执行这个函数,首先把赛车在z方向移动一定量(赛车相对地面是运动的),然后计算比率,把赛车移动到相应的位置并且缩 放。我把它命名updateCar,还是运用摄像机的理论。分别计算出摄像机与小车的xyz距离,然后把小车缩放和移动。注意小车如果离摄像机太远或者被 甩到摄像机的后面的话,我们可以让它不在屏幕上显示。

// for each of the running cycle, these two functions are called
function updateCar(car)
{
    var x 
= car.x_3dcamera.x;             // calculate the x distance between your camera and car
    var y = car.y_3dcamera.y;            // same we can y distance
    var z = car.z_3dcamera.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_3dcamera.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),我们把这个轮胎重新定位到摄像机前非常远的地方。

function updateTire(tire)
{
    var x 
= tire.x_3dcamera.x;
    var y 
= tire.y_3dcamera.y;
    var z 
= tire.z_3dcamera.z;
    
    
if (z < 0)
    {
        tire.z_3d 
+= 10000;               // if the tire is left behind, then offset it 
        z = tire.z_3dcamera.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函数,刷新所有赛车和轮胎的位置和大小。

// 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"160x444444true));
}

10. 下面是键盘响应事件函数,我写了很多的注释在程序里,你应该很快就能看懂,就不打算详细解说了。功能是当按下左键你的赛车左移;按下上键,赛车开始加速(当然极速是需要你来定义的了)等等。

// 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. 最后,添加循环函数执行和键盘响应事件。如果没问题的话,现在发布运行。成功了!

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

 

注意:

当你在循环一个数组中所有对象时,你可能会遇到想要删除一个对象的情况(可能你需要把这个对象从这个数组删除,然后添加到另外一个数组中),那么在 这个删除的过程中你要非常的小心,因为数组在你执行删除操作后的长度会改变,那么你如果循环使用数组长度作为max的话,会造成掠过对现在在原删除对象所 在位置的对象的操作。

一种解决办法就是在循环之前定义一个变量然后再执行for循环。

var length = objects.numChildren;

目前为止,我们一直在讨论3D场景的设置。后面我会陆续的介绍如何使用代码实现3D物体的绘制,不过我想在那之前我们还是多看几个3D场景的例子加深你的印象。那么,请不要失去耐心,最终你所关心的内容这里一定会有介绍的。

点此下载本节源文件

作者:Yang Zhou
出处:http://yangzhou1030.cnblogs.com
感谢:Yunqing
本文版权归作者和博客园共有,转载未经作者同意必须保留此段声明。请在文章页面明显位置给出原文连接,作者保留追究法律责任的权利。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏