3D是非常酷的技術,同時也就意味著更多的工作,上次的簡單介紹之後,這次還要講更多2D到3D的新概念。
基於時間的三維移動
我們使用Vector3類來進行3D上的移動,與2D非常類似,看下面一個例子:
直升機A在(-6, 2, 2)的位置上,目標是直升機B(7, 5, 10),A想摧毀B,所以發射了一枚火箭AB,現在我們得把火箭的運動軌跡過程給畫出來,否則一點發射敵機就炸了,多沒意思啊~~ 通過計算出兩者之間的向量為(13, 3, 8),然後單位化這個向量,這樣就可以在運動中用到了,下面的程式碼做了這些事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from gameobjects.vector3 import * A = (–6, 2, 2) B = (7, 5, 10) plasma_speed = 100. # meters per second AB = Vector3.from_points(A, B) print "Vector to droid is", AB distance_to_target = AB.get_magnitude() print "Distance to droid is", distance_to_target, "meters" plasma_heading = AB.get_normalized() print "Heading is", plasma_heading #######輸出結果######### Vector to droid is (13, 3, 8) Distance to droid is 15.5563491861 meters Heading is (0.835672, 0.192847, 0.514259) |
然後不停的重繪火箭的位置,用這個語句:
rocket_location += heading * time_passed_seconds * speed
不過我們還不能直接在pygame中繪製3D物體,得先學習一下下面講的,“如何把3D轉換為2D”。
3D透視
如果您初中美術認真學了的話,應該會知道這裡要講什麼,還記得當初我們是如何在紙上畫立方體的?
忘了?OK,從頭開始說起吧,儲存、計算3D座標是非常容易的,但是要把它展現到螢幕上就不那麼簡單了,因為pygame中所有的繪圖函式都只接受2D座標,因此,我們必須把這些3D的座標投影到2D的圖面上。
平行投影
最簡單的投影方法是——把第三個座標z座標給丟棄,用這樣的一個簡單的函式就可以做到:
1 2 |
def parallel_project(vector3): return (vector3.x, vector3.y) |
儘管這樣的轉換簡單又快速,我們卻不能用它。為什麼?效果太糟糕了,我們甚至無法在最終的畫面上感受到一點立體的影子,這個世界看起來還是平的,沒有那個物體看起來比其他物體更遠或更近。就好像我下邊這幅圖一樣。
立體投影
在3D遊戲中使用的更為廣泛且合理的技術是立體投影,因為它的結果更為真實。立體投影把遠處的物體縮小了,也就是使用透視法(foreshortening),如下圖所示,然後下面是我們的轉換函式,看起來也很簡單:
1 2 3 |
def perspective_project(vector3, d): x, y, z = vector3 return (x * d/z, –y * d/z) |
與上一個轉換函式不同的是,這個轉換函式還接受一個d引數(後面討論),然後所有的x、y座標都會接受這個d的洗禮,同時z也會插一腳,把原本的座標進行縮放。
d的意思是視距(viewing distance),也就是攝像頭到3D世界物體在螢幕上的畫素體現之間的距離。比如說,一個在(10, 5, 100)的物體移動到了(11, 5, 100),視距是100的時候,它在螢幕上就剛好移動了1個畫素,但如果它的z不是100,或者視距不是100,那麼可能移動距離就不再是1個畫素的距離。有些抽象,不過玩過3D遊戲的話(這裡指國外的3D大作),都有一種滾輪調節遠近的功能,這就是視距(當然調的時候視野也會變化,這個下面說)。
在我們玩遊戲的時候,視距就為我們的眼睛到螢幕的直線距離(以畫素為單位)。
視野
那麼我們怎麼選取一個好的d呢?我們當然可以不斷調整實驗來得到一個,不過我們還可以通過視野(field of view)來計算一個出來。視野也就是在一個時刻能看到的角度。看一下左圖的視野和視距的關係,可以看到兩者是有制約關係,當視野角度(fov)增大的時候,d就會減小;而d增加的話,視野角度就會減小,能看到的東西也就變少了。
視野是決定在3D畫面上展現多少東西的絕好武器,然後我們還需要一個d來決定透視深度,使用一點點三角只是,我們就可以從fov計算出d,寫一下下面的程式碼學習學習:
在Internet上,你總是能找到99%以上的需要的別人寫好的程式碼。不過偶爾還是要自己寫一下的,不用擔心自己的數學是不及格的,這個很簡單~ 很多時候實際動手試一下,你就能明白更多。
1 2 3 4 |
from math import tan def calculate_viewing_distance(fov, screen_width): d = (screen_width/2.0) / tan(fov/2.0) return d |
fov角度可能取45~60°比較合適,這樣看起來很自然。當然每個人每個遊戲都有特別的地方,比如FPS的話,fov可能比較大;而策略性遊戲的fov角度會比較小,這樣可以看到更多的東西。很多時候還需要不停的變化fov,最明顯的CS中的狙擊槍(從沒玩過,不過聽過),開出來和關掉是完全不同的效果,改的就是視野角度。
今天又是補充了一大堆知識,等不及了吧~我們下一章就能看到一個用pygame畫就的3D世界了!