用flash action制作3d效果
春節(jié)以前在論壇上發(fā)布了我剛剛做出的矢量分解3d效果演示的flash,引起了很多網友索要教程,當時許諾春節(jié)后寫教程的,F在把教程寫出來,還不算太晚吧!不過我再多說一句,如果有人想轉載的話,請注明作者和出處。
這個3d效果演示的框架是從去年暑假編寫出的雛形,當時得到了同學張旭的熱心幫助。雖然action腳本有些亂,自己感覺效果還是不錯,于是把源碼mailto了閃客帝國,終于杳無音信了。這個寒假里又需要制作類似的3d效果,終于又完全重新編寫了一遍,畫線使用了goldgoat的連線程序,3d引擎注意了結構化編程,現在的這個版本算是渙然一新了。這其間,得到了QUESTER等網友的建議和幫助,在此一并表示感謝。
在這次發(fā)布前,我又對源碼進行了一遍修改,修正了不妥之處,把所有的action集中到了一個mc里,很詳盡的加進了注釋,這個版本和我給冷語等網友過目的又有很大不同了。雖歷經多次修改,里面可能還有隱藏的bug,希望網友給予批評指正。
廢話少說,我們先來看看效果,為了簡化,我這次發(fā)布的作品不再分析矢量了,保留了坐標軸和XOY平面影格,在空中畫了一個正方形。大家請看:
頁面:點這兒參觀 源文件 (5k):3d7.zip
因為action很長,我只對3d部分做介紹,鼠標感應部分大家自己分析吧!
首先是設置初始狀態(tài),這段action加在主場境中draw3d這個mc上,因為全是用action寫的,看起來好像空mc,呵呵。
onClipEvent (load) { // 設置初始狀態(tài) this.hangle = 45; // hangle:視角和X軸正夾角 this.vangle = 20; // vangle:視角和XOY平面夾角 }
draw3d這個mc里面分為3層,第一層goldgoat-line里面的action是goldgoat所做的畫直線程序,大家可以參看goldgoat的教程:http://m.95time.cn/doc/multimedia/flash/200201/00000284.asp。在這里大家不需要懂,會用就ok了,不再多做介紹。
第二層function是3d引擎的核心,action層是函數的調用,我們先介紹function層。
第一個函數是view3d,注釋已經很明白了,我想我需要解釋的僅僅是算法。紅色的部分是算法核心,通過空間任意一個點在在視角的坐標系投影,得到新的3維坐標,這里新的x、y就是顯示坐標,z是深度。具體這個投影公式我是怎么得到的,大家想弄明白還是自己找立體幾何,反正我也是根據原理自己算出來的。如果大家僅僅是想使用,只要會用就行了。
function view3d (view) { // 3d視角轉換函數,返回值是一個一維數組 // 形參view是一個數組,view[0]、[1]、[2]分別為坐標x、y、z值 var x1 = view[0]; var y1 = view[1]; var z1 = view[2]; var hangle2 = hangle*Math.PI/180; var vangle2 = vangle*Math.PI/180; var x2 = y1*Math.cos(hangle2)-x1*Math.sin(hangle2); var y2 = z1*Math.cos(vangle2)-(y1*Math.sin(hangle2)+x1*Math.cos(hangle2))*Math.sin(vangle2); var z2 = z1*Math.sin(vangle2)+(y1*Math.sin(hangle2)+x1*Math.cos(hangle2))*Math.cos(vangle2); // 以上為計算部分 var fix3d = 1000; x2 *= (fix3d+z2)/fix3d; y2 *= (fix3d+z2)/fix3d; view = [x2, -y2, z2]; // 以上為視角修正部分 return view; // 返回值view是一個數組,view[0]、[1]分別為現實坐標x、y值,view[2]為深度 }
第二個函數是draw3d,作用就是畫空間任兩點之間的直線,這里兩次調用了前面的view3d函數。因此顯然,這里做形參的xyzlist1、xyzlist2是原始XOYZ坐標系里的坐標。在畫線之前,先刪除以前存在的這根線。也許這樣效率不是很高,每次都要重新attachMovie執(zhí)行速度很慢,不過goldgoat沒有提供moveline的功能,我也懶得自己寫(我們在一個項目組,是有分工的)。不過也許是因為這里的影響,在我的PIII866、256M內存的機器上,最多到200條線,鼠標拖動就開始不流暢了,這對用大量線段擬合曲線是不利的,以后升級時可以考慮修改此處,不過200條線,一般也是夠用了。
function draw3d (xyzlist1, xyzlist2, linename, linecolor) { // 畫空間任兩點之間直線函數,無返回值 // xyzlist1、xyzlist2為坐標數組,linename為線的名字,linecolor為線的顏色(可省略) xyzlist1 = view3d(xyzlist1); xyzlist2 = view3d(xyzlist2); this[linename].delete_line(linename); this[linename] = new line(xyzlist1[0], xyzlist1[1], xyzlist2[0], xyzlist2[1], linecolor); }
第三個函數是drawaxis,作用是畫坐標軸,坐標軸最頭疼的地方是有個箭頭,還有一個xyz的字母,坐標軸動,箭頭指向要動,但是字母還要保持豎直,faint!所以這里面又要很唐僧的判斷直線方向,我還以為有了goldgoat的畫線,再也不用寫這個東西了呢,唉——
function drawaxis (xyzlist1, xyzlist2, axisname, axiscolor, axisdeep) { // 畫坐標軸函數,無返回值 // xyzlist1為起點坐標數組,xyzlist2為終點坐標數組 // axisname為坐標軸的名字,axiscolor為坐標軸的顏色,axisdeep坐標軸深度 xyzlist1 = view3d(xyzlist1); xyzlist2 = view3d(xyzlist2); this["axis"+axisname].delete_line(eval("axis"+axisname)); this["axis"+axisname] = new line(xyzlist1[0], xyzlist1[1], xyzlist2[0], xyzlist2[1], axiscolor); // 以上畫坐標軸軸線 this.attachMovie("arrow", "arrow"+axisname, axisdeep); var mycolor = new Color(this["arrow"+axisname]); mycolor.setRGB(axiscolor); this["arrow"+axisname]._x = xyzlist2[0]; this["arrow"+axisname]._y = xyzlist2[1]; // 以上畫坐標軸箭頭、設置位置 var s = 180*Math.atan((xyzlist2[1]-xyzlist1[1])/(xyzlist2[0]-xyzlist1[0]))/Math.PI; if ((xyzlist2[0]-xyzlist1[0])<0) { s += 180; } this["arrow"+axisname].arrow._rotation = s; this["arrow"+axisname].arrow.letter._rotation = -s; this["arrow"+axisname].arrow.letter.letter = axisname; // 以上旋轉箭頭至合適方向 }
第四個函數drawxyz,這個函數就很前面獨立的函數不太一樣了,它不是那種封裝起來可移植的。之所以寫成函數是因為嫌這些代碼太多,在action層太占地方。hoho
function drawxyz () { // 畫空間3軸及XOY平面函數,無參數和返回值 for (var i = 0; i<=10; i++) { if (i<>5) { draw3d([-100+20*i, -100, 0], [-100+20*i, 100, 0], "h_line"+i, 0x999999); } } for (i=0; i<=10; i++) { if (i<>5) { draw3d([-100, -100+20*i, 0], [100, -100+20*i, 0], "s_line"+i, 0x999999); } } // 以上畫XOY平面影格 var axisdeep = 1000; drawaxis([-120, 0, 0], [120, 0, 0], "x", 0xff0000, axisdeep); drawaxis([0, -120, 0], [0, 120, 0], "y", 0xffaa00, axisdeep+1); drawaxis([0, 0, 0], [0, 0, 120], "z", 0x00cc33, axisdeep+2); // 以上畫3坐標軸 }
function層完了,大家是不是暈了?哈哈 action層很簡單了,不再多說了。
if (oldhangle<>hangle || oldvangle<>vangle) { // 如果視角有變化,重畫空間 drawxyz(); oldhangle = hangle; oldvangle = vangle; } // 畫一個正方形,讀者可以依照格式自由發(fā)揮 draw3d([60, 60, 30], [-60, 60, 30], "line1", 0x0000ff); draw3d([-60, 60, 30], [-60, -60, 30], "line2", 0x0000ff); draw3d([-60, -60, 30], [60, -60, 30], "line3", 0x0000ff); draw3d([60, -60, 30], [60, 60, 30], "line4", 0x0000ff);
春節(jié)大家大魚大肉吃的都比較多,希望大家慢慢消化! 不明白之處,我們再討論。不當之處,大家批評指正!
出處:藍色理想
責任編輯:藍色
◎進入論壇Flash專欄版塊參加討論
|