第十五章我們介紹了 3D,但只是將物體置於 3D 空間中,設定大小與位置。物體實際
上還是 2D 的。這就像老的 3D 遊戲中,我們可以繞著某個物體或人物走,這些對象會轉
過來面對我們。這些物體或人物並不是真正的會轉過來 —— 只是看上去是這樣的,因為它
們都是 2D 物體,那是我們看到它唯一的一個視角。
本章,我們將真正地在 Flash 中建立 3D 模型。具體說來有建立並使用 3D 點,線條,
填充以及立體圖形。學習完本章,大家就可以任意在三維空間中建立各種形狀,並對它們執
行移動和旋轉。
建立點和線條
如果只在 3D 中建立點而不建立線條的話是沒有任何意義的。因為理論上講,一個點
是沒有維度並且不可見的。開始時,我們還將使用 Ball3D 類,給一個非常小的半徑,只要
能看到點在哪就夠了。這樣,只需要再建立幾條線將這些小球連起來就行了。相當簡單。我
們前面做過這樣的例子,不過這次要在小球上添加透視,將它們放入 3D 空間。
可以將點的執行個體設為黑色,10 像素寬。建立一些點的執行個體,根據滑鼠的位置對它們進
行旋轉(如前一章最後所講的一樣)然後在點間繪製一些線條。代碼與上一章的 RotateXY.as
檔案幾乎相同。主要的區別在 onEnterFrame 的最後一塊兒,從第一個點迴圈繪製線條到剩
下所有的點。同時,我還刪除了 sortZ 方法,因為在這裡有沒有都一樣。以下是 Lines3D1.as
的全部代碼。
運行結果 16-1 所示。
圖 16-1 3D 點與線
實際來說,沒有太多可改的地方。如果要轉為立體 3D 模型,就要捨棄所有的這些黑
點。第一個嘗試非常簡單。只要在建立時將每個 Ball3D 執行個體的半徑設定為 0 即可,如下:
var ball:Ball3D = new Ball3D(0);
運行結果 16-2 所示。
圖 16-2 3D 線條,點不可見
回過頭再看看先前的代碼, 這時我們發現有很多地方是多餘的。比如,scaleX, scaleY 以
及 visible 對於一個看不到的點來說完全沒有用的。沒有可見的或可縮放的對象。根據這條
線索,更深層地思考認識一下這個繼承自 Sprite 類的一般類,現在它實際上只是五個變數
的容器:xpos, ypos, zpos, x, y。實在有些過分。一個影片有許多功能——發送事件,繪圖,
容納其它 sprite 影片等等 —— 並且佔用了大量資源。如此使用一個 sprite 影片就像開著
坦克到市區裡撿一塊麵包。
如果只需要儲存變數的話,不如讓我們建立一個只用於儲存這些變數作為屬性的類。事
實上, 如果再將透視和座標旋轉再放入這個類中,比單純只儲存幾個屬性更有意義。 女士們、
先生們,現在我把這個 Point3D 類送與各位:
package {
public class Point3D {
public var fl:Number = 250;
private var vpX:Number = 0;
private var vpY:Number = 0;
private var cX:Number = 0;
private var cY:Number = 0;
private var cZ:Number = 0;
public var x:Number = 0;
public var y:Number = 0;
public var z:Number = 0;
public function Point3D(x:Number=0, y:Number=0, z:Number=0) {
this.x = x;
this.y = y;
this.z = z;
}
public function setVanishingPoint(vpX:Number, vpY:Number):void {
this.vpX = vpX;
this.vpY = vpY;
}
public function setCenter(cX:Number,cY:Number,cZ:Number=0):void {
this.cX = cX;
this.cY = cY;
this.cZ = cZ;
}
public function get screenX():Number {
var scale:Number = fl / (fl + z + cZ);
return vpX + cX + x * scale;
}
public function get screenY():Number {
var scale:Number = fl / (fl + z + cZ);
return vpY + cY + y * scale;
}
public function rotateX(angleX:Number):void {
var cosX:Number = Math.cos(angleX);
var sinX:Number = Math.sin(angleX);
var y1:Number = y * cosX - z * sinX;
var z1:Number = z * cosX + y * sinX;
y = y1;
z = z1;
}
public function rotateY(angleY:Number):void {
var cosY:Number = Math.cos(angleY);
var sinY:Number = Math.sin(angleY);
var x1:Number = x * cosY - z * sinY;
var z1:Number = z * cosY + x * sinY;
x = x1;
z = z1;
}
public function rotateZ(angleZ:Number):void {
var cosZ:Number = Math.cos(angleZ);
var sinZ:Number = Math.sin(angleZ);
var x1:Number = x * cosZ - y * sinZ;
var y1:Number = y * cosZ + x * sinZ;
x = x1;
y = y1;
}
}
}
現在,在使用這個類建立 3D 點時,可以傳入 x, y, z 座標,或者使用公用的 x, y, z 屬
性 進 行 設 置 。 類 中 包 涵 了 一 個 默 認 的 fl 屬 性 以 及 用 於 存 放 消 失 點 的 屬 性 , 可通過setVanishingPoint() 方法進行設定。使用三種旋轉方法,可以繞著某一軸的中心點進行旋轉。
還可以用 setCenter 方法改變中心點。大家馬上會看到這些應用。最後,它還可以為我們處
理所有的透視。當我們已經旋轉或設定了點的位置時,只需要用 screenX 和 screenY 即可
獲得透視的點在螢幕上的位置。
這個類會讓我們後面兩章的程式變得簡單許多。
使用這個新的類重新編寫上麵線條旋轉的程式將會驚人的簡單,如 Lines3D2.as 所示:
圖 16-3 3D 空間中的正方形座標
注意,所有的 z 值都相同。這是一個平面上的正方形。將一個正方形放在平面上的最
簡單的辦法就是讓所有的點在某個軸上的位置都相同(這裡我選擇了 Z 軸),還要定義正方
形的其它兩個軸(x 和 y 軸)。以下代碼將代替迴圈隨機建立的點:
points[0] = new Point3D(-100, -100, 100);
points[1] = new Point3D( 100, -100, 100);
points[2] = new Point3D( 100, 100, 100);
points[3] = new Point3D(-100, 100, 100);
我們還要手動設定每個點的消失點。在迴圈中做是再簡單不過的了:
for (var i:uint = 0; i < numPoints; i++) {
points[i].setVanishingPoint(vpX, vpY);
}
因為現在有四個點,我們將不得不把 numPoints 改為 4。剩下的代碼還可以正常工作,但
是我們還要多做一步:繪製一條串連最後一個點與第一個點的線,用於將圖形封閉。以下是
全部代碼,可見 Square3D.as(運行結果 16-4 所示):
圖 16-4 3D 旋轉正方形
太奇妙了——一個旋轉的正方形!現在我們應該可以建立出任何的平面圖形了。我通常
都預先將所有的這些點在方格紙上標出( 16-5 所示) ,用來協助作圖。
圖 16-5 使用方格紙標出所有的點
通過這個草稿,可以用來協助建立所需的這些點:
points[0] = new Point3D(-150, -250, 100);
points[1] = new Point3D( 150, -250, 100);
points[2] = new Point3D( 150, -150, 100);
points[3] = new Point3D( -50, -150, 100);
points[4] = new Point3D( -50, -50, 100);
points[5] = new Point3D( 50, -50, 100);
points[6] = new Point3D( 50, 50, 100);
points[7] = new Point3D( -50, 50, 100);
points[8] = new Point3D( -50, 150, 100);
points[9] = new Point3D( 150, 150, 100);
points[10] = new Point3D( 150, 250, 100);
points[11] = new Point3D(-150, 250, 100);
這樣就完成了 SpinningE.as 中的旋轉字母 E 的效果,運行結果 16-6 所示。不要忘記
將 numPoints 改為 12。稍後我們要使用 points.length 讓這個值可以動態改變。
圖 16-6 3D 旋轉字母 E
執行後我們注意到當字母 E 向我們旋轉過來時,某些點會變得非常近,並且與第一個
3D 例子一樣會發生倒置。您也許首先會想到增加所有點的 z 值,但這實際上會讓效果變
得更糟。例如,假設將 z 設為 500。旋轉的時候,z 將從 500 到 -500,甚至會讓有些點
運動到觀察點更往後的位置,會讓事情更糟糕。不過我們可以使用 setCenter 方法將所有點
向 Z 軸推移,如下:
for (var i:uint = 0; i < numPoints; i++) {
points[i].setVanishingPoint(vpX, vpY);
points[i].setCenter(0, 0, 200);
}
開啟 Point3D 類,我們會看到 scale 的計算方法為:
var scale:Number = fl / (fl + z + cZ);
這樣就將整個系統推移了 200 像素,包括旋轉系統。原來 z 的值保持不變,只是因為計算
透視的值更高了,那麼所有物體都將呈現在觀察著的前面。試用其它的值進行替換,觀察運
動效果。稍後,我們會讓這個圖形在其它的軸上也能運動起來。
(如果要轉載請註明出處http://blog.sina.com.cn/jooi,謝謝)