前言

射擊遊戲 (Shooting Game) 源自日本,

(由Taito公司的 西角友宏 獨自耗時三個月開發完成)

在二十年前,曾經是電玩遊戲的代名詞

從「Space Invaders」到「Gradius」...「R-Type」.....

從十幾年前發跡的「雷電」系列,到近幾年的「東方系列」

我們可以發現這是一個歷久不衰的遊戲類型。

 

射擊遊戲,在近十幾年由於微算機處理速度的飛快進展,畫面同時顯示的子彈倍增

有了一個更誇張的名稱叫做「彈幕遊戲」

但無論如何,只是子彈多了點,演算法更先進、華麗了點

基本上的遊戲引擎還是大同小異。

希望藉由本文,能讓我們來了解,一個射擊遊戲的設計方式為何

並且讓程式設計師能夠寫出屬於自己的彈幕遊戲。

 

控制子彈移動的主要方式

無論是哪一款射擊遊戲,你只要盯住任何一顆子彈看

會發現單一顆子彈的走向是固定的

從這一點,我們就可以稍微破解子彈發射的奧秘

property.jpg

事實上,每一顆子彈都被賦予一些固定的屬性

分別是「現在 X Y 位置」、「移動速度」、「移動角度」等等

而表示移動方位的方法,又有兩種

如下圖所示

coordinator.jpg

上圖中綠色代表速度,橘色代表角度

這兩種表達方式,具有微積分或三角函數概念的,就能理解

左方的表達方式是「極座標系」(Polar Coodrinate),

右方的表達方式是「直角坐標系」(Rectangular Coordinate)

一般來說我們為了設計方便 (尤其是為了設計華麗的彈幕布局)

會採取極座標系來表達子彈的走向

因為只要給予速度和角度,就能讓程式去執行

 

然而極座標系的表達方式,電腦是沒辦法直接理解的

電腦充其量只能執行直角坐標系 (必須要把X變量和Y變量寫得一清二楚)

所以我們要把一段數學式寫成程式,把 極座標系 轉換為 直角座標系

tr.jpg 

只要在每一個畫格裡

將子彈的 x 和 y 分別加上 x_diff 和 y_diff

程式就能順利執行你的子彈動作了


 

子彈命中的 衝突判定

無論是彈幕遊戲也好,或者是其他各種類型的遊戲

有一個很重要的物理運算即是「衝突判定」

一般來說,正常的自然世界上,兩個東西不可能佔據同一個空間位置

也就是說世界上任兩樣固體,不可能佔據同一個位置,不可能會穿透過去

當兩個物件想要存在同一個空間位置的時候,就會發生碰撞

這就叫做衝突

crash.jpg
任兩台碰碰車想要佔據同一個物理空間位置時,就會發生「衝突」

當「子彈」和任何一個物件「發生衝突」的時候

實際上就是擊中目標了!

在彈幕遊戲裡的衝突計算,大致像下面這張圖這樣

co1.jpg
( 藍色線較兩者橘色半徑總和長,無碰撞 )

co2.jpg
( 藍色線小於兩者橘色半徑總和長,碰撞 )

也就是說當 ( 兩個物件的半徑和 > 兩個物件的中點距離 )

就會發生衝突

 

另外,雖然我們的物件圖形,大部分是複雜的多邊形

為了簡化運算,提升效率

我們會把它的「判定點」簡化為多邊形

如下圖所示

det.JPG

當然,如果你的子彈實在非常小顆

那麼就把子彈當做「點」來處理就好,可以省掉更多運算

運算的方法更簡單

只要你的「點」不是卡在對方物件裡面

就等於沒有碰撞到

c3.JPG


儲存所有子彈的 資料結構

從剛剛介紹到現在為止,只告訴你對於單一子彈的操縱

尚未探討到畫面有多顆子彈的情況下

要如何儲存下來

這時候設計者就要開始考量

我要在畫面上呈現「有限數量子彈」還是「無限數量子彈」

 

 

 

有限數量 

無限數量

實現難度

較簡單

較困難

執行效率

固定

視子彈多寡而變化

資料結構

Array

Linking-List

未上場的子彈

隱藏在畫面外面

未上場則不存在

 所謂的「有限數量」是用(Array)去實做,優點是實做方便,
缺點是一開始就要佔用固定大小的記憶體,而且最大的數量是固定的

而「無限數量」是用(Linking-List)去實做,
優點是可以發射到無限發子彈 (在記憶體用盡之前),缺點是較難實做

兩種資料結構的儲存方式大致如下

1. Array
array.JPG
以Array(陣列)資料結構儲存各個子彈的資訊 (No.99那發未上場,不顯示也不移動)

2. Linked-List 
link.JPG
以Linked-List(串列)資料結構儲存各個子彈的資訊

將所有的資料儲存之後,接著就是用 for 迴圈依序去處理每一個子彈的動作

有程式撰寫經驗者,應該知道如何處理才是。

「發射子彈」的方法

隨著剛才的資料結構設計不同

發射子彈的方法也不同


如果選擇Array實做

那麼就是將 要被發射的子彈的 座標 重新指定於螢幕範圍內

並且重新指定它的速度和角度,將其「活化」,變成會動的

hidden.JPG

 

如果選擇 Linked-List實做

則是很簡單的,把一個新的子彈物件 加入到List裡面即可

hidden2.JPG  

 

射擊遊戲運算引擎

multi.JPG 

基本上,遊戲設計的概念和視窗程式設計很雷同

必須有一個 無限迴圈 等待訊息進來,然後處理

但是遊戲設計又比較麻煩

因為遊戲是,不管你有沒有訊息進來,我的畫面都要作運算

因此遊戲程式的無限迴圈 不單單是等訊息進來

而是無時無刻的做圖形運算

「順便」等待訊息進來。

engine.JPG

而這邊我們討論的是射擊遊戲

射擊遊戲需要被運算大宗,就是子彈

因此在單單一個畫格(frame)中

程式就要針對「每一個」子彈去做運算

算出它們應該走的位移向量,並且把每一個的位置都顯示出來

迴圈每做一次(one iteration),就要做這整件事情一次,就如同上圖所示的一樣

 

「追蹤彈」的設計

tracex.JPG 

追蹤彈,又稱追尾彈,簡單的說就是追蹤導彈

 trace.JPG 

追蹤導彈為了要逼真

就要有精心設計的演算法

這邊主要介紹兩種

第一種是自己想到的,比較簡單,適合初學者

主要是在每個畫格中,執行此兩個步驟

 

1. 算出和對方的距離為何

2. 追蹤者 把自己的位置加上 該距離的 1/n

 

剛才那個 n 就是我們的分母

分母越大,就越容易閃躲

反之則是幾乎無法躲掉

端看你的難度要設計如何

  

然而大家要知道,追蹤目標是會移動的

所以每個畫格都要計算和對方的距離為何

執行的結果如下圖所示

trace2.JPG 

  

另外一種做法 做起會比較逼真

運用到的是軍事用途上「追熱彈」的概念

在真正的戰爭上,如果一發導彈要追敵機,它所追的是 「熱」

因為戰鬥機會發熱。

heat-seek.PNG

我們擬制一個座標系

(0,0)位置代表熱的中心,也就是敵機的位置

設 平面上各點的熱度為 T(x,y) = 20 - 4x^2 - y^2

如果你要算出當下(2,-3) 如何移動 才可以最有效率擊中敵機

則要算出該點的direction of maximum rate of increase

算法是 求出 T(x,y) ,也就是分別對 x y 做偏微分 求出梯度

得到 T(x,y) = -8xi -2yj

將 (2,-3) 代入

T(2,-3) = -16i + 6j

是以該子彈該當下朝 (-16,6) 的方向移動 會最有效率


結語

射擊遊戲的設計,核心並不如其他遊戲複雜困難

然而其創意的發展卻是無限的

無論是系統本身、子彈的發射圖形安排

可以用到的創意是無止盡的

期望本篇文章能給 對射擊遊戲設計不知其門而入的 程式設計師

有重大的概念啟發


參考書目

Java Game Programming : 建立遊戲的演算法與框架/ 長久勝著 博碩出版

Calculus 8th Edition / By Larson, Hostetler, Edwards/ Houghton Mifflin

 

延伸閱讀

http://v.youku.com/v_show/id_XMTA3MjUxMDM2.html

 

版權聲明

本文僅供教學參考之用,非用於商業用途

本文歡迎各類專題論文參考引用,唯須註明出處為 Frank's Ivory Tower,謝謝。


arrow
arrow
    全站熱搜

    finalfrank 發表在 痞客邦 留言(1) 人氣()