AirHockey ThreeD 氣墊球 3D Computer Graphics and Visualization Final Project AirHockey ThreeD 氣墊球 3D 496410060 張庭翰
Introduction AirHockey ThreeD,是一個用OpenGL撰寫的一個簡單的氣墊球(Air hockey)遊戲。 規則很簡單,只要操控球槌(綠色),把球(紅色)打進對方的球門裡,就算得分。 操控方式: 在視窗畫面內點擊滑鼠左鍵,在視窗內移動滑鼠,即可操控球槌,再按一下左鍵,即取消操控。 按住滑鼠右鍵拖曳,可改變視角。 鍵盤R(r)鍵,重設分數。 鍵盤M(m)鍵,切換第三人稱(正常)視角或第一人稱視角。
Functions and Features 功能 說明 文字顯示 顯示分數與比賽結果。 物件繪製 利用內建的函式繪製立體幾何模型,加上手動定義的多邊形,湊出遊戲中的各種物件。 攝影機位置與視角模式 利用在作業三時練習的攝影機座標控制,加上簡單的幾何概念,實現可用滑鼠拖曳任意調整視角。兩種視角模式。 打光和材質 在作業五,練習了如何設置光源及材質。為使打光效果更加逼真,使用了一個小技巧,來達成類似Phong Shading的效果。 位置與速度計算 運用基礎的物理概念,定義速度的計算。 碰撞偵測 簡單的偵測球和牆壁、球槌是否發生碰撞。 反射計算 給定入射速度向量及碰撞點法向量,求出反射速度向量。 摩擦力與能量消耗 考慮現實生活中非理想的情況,加入能量的衰減。 電腦策略 定義簡單的策略控制敵方球槌,使玩家可以和電腦對戰。 程式擴展性、彈性 實作時用object-oriented的概念,將屬性參數包在class中,以便未來調整,擴充。
文字顯示 ◆要知道遊戲的狀況,就必須在畫面上顯示文字,呈現雙方分數和是否進球。見下圖用紅色框起處。 ◆文字的呈現在作業一時已經練習過了,使用"glutBitmapCharacter()"這個function印出各個字元。要印出某個變數的值只要用"sprintf()"將變數印到字串中再顯示即可。 ◆因為文字必須"貼"在畫面上,不能受到攝影機的影響,所以必須先重設matrix,然後設定顯示文字的攝影機的位置(0,0,3),然後才印出文字。 ◆文字不想受光照影響,再繪製前先關閉lighting。
物件繪製 - 基礎幾何圖形元件 ◆遊戲中的各個物件,皆是由內建的幾何圖形,及自定義的多邊形組合而成。手動定義描點的部份包括圓和環。 ◆圓形平面的繪製,在作業一中已練習過,cos(theta)代表x的值,sin(theta)為y的值,theta從0遞增到2PI即可畫出圓。 ◆環是圓的延伸,為一空心的圓。我用三角形拼成,在畫圓時加入一個內圈的點,形成直角三角形。第一個loop從外 圈開始繞,如下圖紅色部分。第二個loop 繞內圈,連接外圈的一個點,如下圖 綠色部分,如此就能畫出一個環。
物件繪製 - 球槌 (mallet) ◆利用幾何圖形間相對位置的概念,及座標系統的轉換(rotation、translation...),畫出各個物件。 ◆球槌由一個球(sphere)、三個圓柱(cylinder)、一個環和一個圓(底部)組成。
物件繪製 - 球 (puck) ◆球是由兩個圓柱(cylinder)兩個圓組成。
物件繪製 - 桌 ◆桌子可分成三個部份:桌面(藍色)、邊框(銀白色)和圖案(白線和球門)。
物件繪製 - 桌面 ◆桌面粗略來看為一個長方形,用四個vertex即可描述。 ◆但我將一個大桌面劃分成多個小矩形,增加內部vertex主要是為了較好的打光效果,在打光的部份會在詳細解釋。
物件繪製 - 邊框 ◆以數個水平及垂直的矩形組成。 ◆由於打光效果較桌面不重要,所以一個大矩形僅分一至二個小矩形組成。
物件繪製 - 圖案 ◆中間白線分個成數個小矩形組合而成。 ◆中央圓環為一個環。 ◆兩側球門的標線受光照影響不大,因此各由三個矩形組成。 ◆球門(黑色)的部份,各由一個矩形組成。 ◆圖案若和桌面/牆面在同一平面時 ,圖案會被桌面/牆面吃掉。因此 要遠離一個極小的位移。 ◆兩側球門不想受lighting的影響, 在繪製時先暫時關閉lighting效果。
攝影機位置與視角模式 ◆這個自由移動視角(攝影機位置)的功能,在作業三時就已經實做出來了。重點在於如何用水平角度(horizontal_angle)、垂直角度(vertical_angle)和球半徑(R_around)這三個變數來描述三度空間中的座標。 ◆只要改變horizontal_angle和vertical_angle 這兩個變數就可以使攝影機在半徑為 R_around的球面上移動。 ◆定義glutMotionFunc偵測滑鼠移動 的x和y值,分別改變水平角度和垂 直角度的值,藉以實現以滑鼠拖曳 控制視角的功能。
攝影機位置與視角模式 (cont) ◆若要將球面座標系統轉換成一般的三維直角座標系統,可透過下面的方程式轉換。 ◆要先知道點所在切面的原半徑 r : r = R_around * cos(vertical_angle) ◆接下來就可以輕易地求得座標值了: cameraX = r * sin(horizontal_angle) cameraY = R_around * sin(vertical_angle) cameraZ = r * cos(horizontal_angle)
攝影機位置與視角模式 (cont) ◆攝影機在球面座標系上的為第三人稱(一般)模式。我還實做了另一個視角模式─第一人稱模式。 ◆第一人稱模式將攝影機設在球槌後上方,隨球槌移動。 ◆按下M(m)鍵可在兩種模式間切換。
打光與材質 ◆在作業五了解如何設定光源和材質。 ◆球槌、球、桌面、邊框、圖案各有各自的材質參數。各參數的決定由實際呈現出的效果不斷調整而決定。 ◆共有三個光源,位置如圖所示。 ◆在繪製物件時指定正確的normal ,使光照可以正確呈現。
打光與材質 (cont) ◆Phong shading是為多邊形內任一點內插端點的法向量,有較好的光照效果,但因Phong shading較慢,因此openGL並沒有內建shong shading。 ◆將一個大平面分割成數個小平面,藉以增加內端點。當分割的夠細時,其效果和Phong shading相同。 ◆一平面上各點法向量相同,因此令每一個小矩形的頂點的法向量皆和四個端點相同。 ◆分割矩形的數量取可接受效果的最小值。以不影響遊戲流暢度為優先。各部份分割的數量不同,因為桌面受光照影響最大,因此畫得最細。
打光與材質 (cont) ◆有無分割成小矩形的差異:
位置與速度計算 ◆玩家方球槌速度由位置決定(滑鼠移動位置),球及電腦方球槌位置由速度決定(給定速度後依速度向量移動位置)。 ◆速度(向量)為位置的微分,即單位時間(每run一次timer的時間)內的位移。 v(t) = s'(t) = s(t) - s(t-1) ◆若給定速度求位移(如敵方球槌和球),新的位置即是現在位置加上速度的向量。即: s(t+1) = s(t) + v(t)
碰撞偵測 ◆因為球和球槌都是圓的,因此碰撞偵測也很單純:當兩者的圓心距離小於等於兩半徑_時,判定為碰撞。 ◆球的碰撞可分為兩種:一是撞到牆壁(邊界),二是撞到球槌 ◆撞到牆壁(邊界)的判斷相當簡單,當圓心位置的x(左右)方向和z(前後)方向分別加/減球半徑後,若比水平邊界或垂直邊界來得大/小,即,超越邊界,此時判定為碰撞。 (x+r)>=右邊界 OR (x-r)<=左邊界 OR (z+r)>=前邊界 OR (z-r)<=後邊界 ◆因為球和球槌都是圓的,因此碰撞偵測也很單純:當兩者的圓心距離小於等於兩半徑_時,判定為碰撞。 ( (xm - xp)2+(zm - zp)2 )0.5 <= rm + rp
反射計算 ◆當球與其它物體發生碰撞時,就伴隨著反射的發生。 ◆所謂反射是指已有碰撞瞬間的入射速度向量,求出反射後的速度向量。 ◆牆面的反射比較單純。在水平(x方向)的牆面發生碰撞時,vx不變,vz方向相反(變號)。 ◆在垂直(z方向)的牆面發生碰撞時,vz不變,vx方向相反(變號)。
反射計算 (cont) ◆如下圖,v為入射速度向量,v'為反射速度向量,N為接觸點法向量。 ◆入射速度v是球的移動速度加上球槌的移動速度。 ◆若是與球槌碰撞,就比較複雜了。 ◆如下圖,v為入射速度向量,v'為反射速度向量,N為接觸點法向量。 ◆入射速度v是球的移動速度加上球槌的移動速度。 ◆N可利用球的位置減球槌的位置簡單地求出。N = pp - pm
反射計算 (cont) ◆向量關係可簡化成下圖。 ◆-v + v' = 2*vN。vN是-v在N上的投影。可推得v' = 2*vN + v,因此求得vN後就可以求得v'。 vN vN
反射計算 (cont) ◆已知-v和N的內積,為-v在N上的投影(vN)長度乘上N的長度。 ◆得知|vN|後即可求出vN。 vN = |vN| * N / |N| ◆得到vn後就能得到反射向量:v' = 2*vN + v
摩擦力與能量消耗 ◆球碰撞到物體,反射後的速度沒有削減的情況太過理想。 ◆考慮到現實的情況,在發生碰撞後,其中一部分能量轉換成形變、熱等其它形式逸散,僅一部份的的能量用於移動。 ◆我假設當球撞到物體時,有四成逸散,僅留存六成速度反射。球槌移動的速度逸散兩成,留八成。 ◆桌面也不該是理想光滑平面,因此也模擬摩擦力的作用,以一微小的反方向加速度作用於球,使其緩慢減速。
電腦策略 ◆設計一簡單的規則,使電腦可以和玩家對戰。 ◆控制的是電腦的球槌速度向量。 ◆水平方向跟著球和自身的位置調整,往球的水平方向移動。 ◆垂直方向分成兩個大情況:1.球在玩家範圍內(電腦碰不到),2.球在電腦範圍內(電腦碰得到)。 ◆在"1"的情況下,會慢慢退回基準線(一開始的位置)。 ◆"2"的情況可細分成兩種子情況:2-1.球在球槌前,2-2.球在球槌後。 ◆"2-1",若球槌和球的水平位置差小於兩者半徑和,判定為"撞得到",向前做等加速度運動。 ◆"2-2",球在球槌後,迅速往後移動。
程式擴展性、彈性 ◆物體的各項參數,如:半徑、速度、位置、材質、長度等,都包在物件的class裡。 ◆物件的繪製都是相對的,且基於物件的半徑、長度等參數。 ◆考慮到未來的擴展性,如場上新增小道具,吃到的話或者球會瞬間加速,改變方向,或者改變場地、球、球槌或球門的大小等。在我的實作裡,要實現這些功能,是非常容易的。
DEMO 初期版 Try it! 最終版
心得感想 當初想做這個題目的原因,是想做一個簡單,但是好玩的東西,譬如說是貪食蛇之類的,但是貪食蛇之前的學長好像有做過了,所以就選了這個氣墊球的遊戲。 一路上大都沒什麼大困難,反射的部分雖然有些麻煩,但是在上網找到反射的計算方式,並自己理解、嘗試過後,就沒什麼大問題了。除此之外最麻煩的還是遊戲的順暢度。 我做這個題目的其中一個目標是希望這個遊戲玩起來能和真實的氣墊球很像,因此找了不少影片與遊戲,參考裡面球的速度、反射後的減速、摩擦力和物件造型等等,藉此調整程式內的各項參數,也不斷調整畫面的呈現,如光源要擺在哪裡,各物件的顏色、材質等等。遊戲玩起來順暢、好玩才是這個project的最大目的。 遊戲做好之後就四處給人試玩,也得到一些反映和意見。利用課程裡學到的openGL programing做一個3D小遊戲,真的還滿有成就感的。
Reference Air Hockey Flash Game 反射向量的计算 Air hockey (Wikipedia) http://www.maniacworld.com/air-hockey-game.htm 反射向量的计算 http://blog.csdn.net/happy__888/archive/2007/03/29/1545432.aspx Air hockey (Wikipedia) http://en.wikipedia.org/wiki/Air_hockey Air hockey (Youtube vedio) http://www.youtube.com/watch?v=nWqr6IxSP-c