夢開始的地方——FC游戲開發(fā)指南(9終)碰撞檢測 & 系列總結
(本系列是一個回歸電子游戲原點的特別系列,作者 @goodorc_gamedev)
上期鏈接:https://www.bilibili.com/read/cv13814284
一眨眼,本系列已經(jīng)來到了最終章。
本章討論最后一個技術問題——碰撞檢測難題,之后就是對本系列的總結。
1、為什么碰撞檢測是一個難題
在玩FC游戲的過程中,每一幀都可能發(fā)生各種碰撞。具體有:
角色與場景的碰撞。
主角與敵人的碰撞。
敵人被主角技能、子彈擊中。
主角被敵方技能、子彈擊中。
這些碰撞都需要被檢測出來。根據(jù)游戲類型來說,碰撞分為兩類:
1、超級瑪麗類,一個主角,場景中有很多碰撞區(qū)。

2、《1942》類,主角有很多子彈,敵人也有很多。每個子彈都會和每一個敵人發(fā)生碰撞。

以1942、1943等打飛機游戲為例,如果玩家同時能發(fā)射6顆子彈,同屏會出現(xiàn)20個敵人。那么如果采用常規(guī)碰撞檢測法,也就是判斷每個子彈和每個敵人的距離,需要用雙重循環(huán)做20*6=120次判斷。
120次很多嗎?對FC孱弱的CPU來說,真多,太多了,根本跑不動。
讓我最佩服的是《魂斗羅》等游戲,不僅敵人多、場景復雜,甚至還能發(fā)射超多子彈,不愧是FC中期到后期的3A大作,優(yōu)化真是絕了。

2、位遮罩(BitMask)碰撞檢測技術
不平凡的問題需要不平凡的解決方案。
對FC來說,CPU很弱,但全屏Tile數(shù)量不算太多,一屏只有32 * 30 = 960個tile。
那么容易想到一種量身定做的解決方案:BitMask 位遮罩檢測技術。
這種思路本質比較樸實——將屏幕按照tile劃分為32列 * 30行的網(wǎng)格,每個區(qū)域用一個bit表示能否被碰撞。沒有碰撞體填0,有碰撞體填1。
這樣,當主角的位置是(100, 100)時,因為100/8 = 12,所以他位于第(12, 12)格,只要判斷col_map[12][12]是1還是0,就可以判斷它能否碰撞了。
借用油管上的一張圖:NES Assembly: Ep7 - BitMask Collision Detection

畫個示意圖比較明顯:

上圖所有可以跳上去的碰撞體,我都用紅點標出來了,最小單位為8像素,也就是一個tile。
射擊游戲的精度可以更低,對《1942》來說,哪怕是16x16的格子也是夠用的。
順帶一說,在游戲引擎開發(fā)中,你經(jīng)常會看到這種把屏幕劃分成格子的優(yōu)化算法,連胡淵鳴大神寫的物理模擬算法也有相似的思路:

3、編程難點:用u8類型的數(shù)組模擬二維bit數(shù)組
眾所周知,C語言并沒有bit二維數(shù)組這種東西。所以咱們只能用u8數(shù)組+位運算來模擬。
這寫起來就有點頭疼了,摘抄我寫的局部代碼:
不瞞你們說,這段檢測代碼我寫了三遍以上,修改了N次(FC開發(fā)不好調試)。最后還是有一點小BUG,所以以上代碼僅供參考。
碰撞檢測的方法先簡單介紹到這里,如果想深入了解,建議參考我的spitfire游戲源碼。地址:http://fogota.ys168.com/ 里面的訪客上傳03文件夾,spitfire壓縮包。
終:連接遙遠的過去與未來
FC游戲開發(fā),是維京的夢,是我的夢,也是所有經(jīng)歷過那個時代的玩家共同的夢想。

FC為我們這一大群人打開了電子游戲的大門,相信其中的每一個人不僅曾經(jīng)熱愛過,也很好奇FC游戲開發(fā)的原理,想知道如何才能做一款FC游戲。就算沒有能力去做,也想多了解一點,知道個大概。
于是我才寫出了這9篇文章,從硬件到軟件,由淺入深,逐步介紹我現(xiàn)階段的研究成果。通過這9篇文章,大家對用C語言開發(fā)FC游戲應該已經(jīng)建立了粗淺而完整的認識。
由于個人能力有限,只能簡單介紹到這里為止。最可惜的是,除了“spitfire”演示項目外,我也還沒能做出像樣的FC游戲作品。也許這個遺憾能夠在未來得到彌補。
這是系列的完結,也是新的開始。感謝所有FC愛好者的捧場,期待再次相會~~
(本文作者?@goodorc_gamedev。歡迎加入游戲開發(fā)群歡樂攪基:1082025059
對游戲開發(fā)感興趣的童鞋可戳這里進一步了解:http://www.levelpp.com/)