Pseudo 3D 賽車遊戲基礎

傑夫老師又被點歌,要做一個 pseudo (假) 3D賽車遊戲,天哪!這是要複刻大型電玩遊戲,老師不是做遊戲出身的,這.....投降可以輸一半嗎?這.....可以怪全國賽出題老師嗎?

不過點歌的也是位老師,明確的說了想要瞭解這三個問題:

  1. 賽車道路的直線加速與彎曲場景的交替變更是如何做出來的?

  2. 如何做到樹和建築物順著指定的道路曲線向後移動?

  3. 在追逐中,道路會出現前車移動擋路,有時超越,有時追不上,這個效果是如何做出來的?

傑夫老師只能求救,請Scratch大神 Cliff Davies (a.k.a. RokCoder) 在 FB 及他的影片中留言,請大神出教學影片造福眾生。

遊戲範例:https://scratch.mit.edu/projects/102723680/

大神很快就回覆,但不是說要做教學影片,而是給了一個通往極樂世界的方向,要我去看他 Scratch 專案的備註與謝誌中有兩個網頁,這兩個網頁說明了3D轉2D的原理。

RokCoder的回覆

RokCoder的回覆

傑夫老師看不到3分鐘,就看不下去了...... 神哪~請再多給我一些天份!不過,傑夫老師找到下面這個影片,此影片使用 C++ 在短短 3 分半鐘把道路、路樹簡單做完,又是一個大神呀!

傑夫老師果然看不懂英文,只看的懂程式,拿著這個影片所附的 source code 再回去對照 RokCoder 提供的兩個網頁,好像就能稍稍懂了。那就來不求甚解的把這個 Pseudo 3D 道路與招牌的部分試著做出來吧!

下面就來簡單說明幾個重點:

3D 投影 2D

先看到下面這段數學公式,這就是在把一個三維的點(x world, y world, z world) 投射到二維螢幕(x screen, y screen) 上。這利用到基本的相似三角形邊長比例,並不困難,有興趣的同學老師可以提供其他影片說明。

投影公式

投影公式

注意:在一般程式語言中,原點 x=0, y=0 是在螢幕的左上角,與 Scratch 在舞台中間不同。

在專案中是這個函式:

注意到每一段路的參數 (x, y, z, X, Y, W) 分別用了清單來紀錄,而每一段的 z 也就是距離等於 n*segment legth 。

畫出直路

如果,把一條固定路寬 (road width) 的筆直道路每隔固定距離 (segment length) 畫一條橫線,再將這條路投射到螢幕後,路中央的座標 (x, y, z) 轉換為 (X, Y),而橫線寬 W 會隨著距離越遠而越短。所以,每段 (segment) 路在螢幕上就是一個一個梯型。

直路投影

直路投影

在專案中畫梯型的函式:

畫梯型的函式

這裡直接借用 Racy Brum Brum 裡面的函式 Render polygon 用畫筆的方式畫出梯型。x1, y1, w1 就是上右圖的 X, Y, W 而 x2, y2, w2 就是上面一條線的中點螢幕座標與線寬。參數 c 是顏色值。

接著只要畫出最靠近的 18 個梯型,就可以了

畫出最靠近的 18 個梯型

drawQuad 傳入參數 (color, line.X(i-1), line.Y(i-1), line.W(i-1), line.X(i), line.Y(i), line.W(i))

畫出

彎路的概念如下圖:

彎路的概念

每一段路都需要一個 curve 變數記錄自己的曲度,而一條彎路的路中心點 x 隨著 curve 等差變化。curve 變數有以下特性:

  • 負值代表右彎

  • 正值代表左彎

  • 值越小表示曲度越小

  • 值越大表示曲度越大

而只要稍稍修改 drawRoad 程式,加上紅線的幾個地方就行:

稍稍修改 drawRoad 程式

到這邊就能做出跟影片中一樣的效果了

加上路邊招牌(或路樹)

說實話這段傑夫老師沒有搞很懂、沒有做很好,主要是在座標轉換與縮放沒弄清楚。

說穿了就是在一段路的路邊按投影比例畫上招牌,在 Scratch 中就利用蓋章功能,從遠而近畫過來。

每段路需要多兩個變數(清單)記錄,line.signType 記錄造型名稱、line.signX 紀錄路寬的比例(大概吧)。原始的程式如下:

clip 的部分被忽略,就只求出 destX, destY, destW, destH

clip 的部分被忽略,就只求出 destX, destY, destW, destH

改編後 Scratch 程式如下:

改編後 Scratch 程式

這樣,就能把路邊招牌畫出來了~

總結

回答原來的問題:

1. 賽車道路的直線加速與彎曲場景的交替變更是如何做出來的?

要有好多個很大的清單記錄每一段路的參數 (x, y, z, curve) 記錄下一整條路。如果要繞成一圈,那在前進後退時加上邊界判斷,讓最後一段路接上第一段路就可以了。

2. 如何做到樹和建築物順著指定的道路曲線向後移動?

其實樹與建築物就定位在一段路的左或右的任一邊,跟著路前進後退,不過要按投影比例縮小放大。

3. 在追逐中,道路會出現前車移動擋路,有時超越,有時追不上,這個效果是如何做出來的?

請教 Cliff 後,他說他在遊戲開始時就會建立對手車輛的位置與速度,當賽車接近到一段距離後就會出現。而在賽車模式下,有程式會讓對手車輛變換車道,不會擋到賽車。

在傑夫老師的專案中,沒有設定賽車的速度,在過彎時賽車也就跟著過彎(並不會直直衝出道路),這表示還有很多要做的。更不要說還要加上對手車輛、碰撞判斷、計時等等,專案離一個遊戲還很遠很遠。有志者事竟成,各位如果有心,可以從這裡開始繼續做下去,網路上還有很多資源可以學習,加油!Bye now~

註:文中說明圖片取自前面所提及之影片或網頁