基於瓦片式地圖的2D遊戲視野機制


3樓貓 發佈時間:2023-07-23 20:58:39 作者:戰術小強 Language

視野的概念本身來源於現實世界,而它的底層規律則是物理意義上的光線傳播機制,即光線沿直線傳播且會被物體遮擋形成陰影,當特定的光線被遮擋無法抵達觀察者眼睛時,即表示該部分視野被遮蔽。
如果是第一人稱遊戲,那麼視野這個概念和機制的存在是理所當然且符合直覺的,只要場景內的物體和角色位置安排正確,渲染過程正確且光線傳播符合物理定律即可。
但考慮第三人稱遊戲時,視野機制就會變得有些特別。
因為在第三人稱狀態下,玩家將可以看見“視野”本身的存在,根據遊戲的具體設定,這個“視野”可以是一個圓形範圍,也可以是一個扇形範圍,甚至可以是三角形範圍。
於是視野問題就轉變成了如何定義並計算出這個“範圍”,通常而言這個“範圍”會被簡稱為FOV,即Field of View,可視範圍。
在討論瓦片式地圖下的特殊視野機制前,應當先考察更一般的情況,即普通的第三人稱2D遊戲中,FOV的計算到底該怎麼做,而作為思路的開端,我們可以基於兩個前提設定進行討論。
  1. 玩家的視野等同於以玩家為原點,360度放射的光線所照亮的區域。
  2. 能夠遮擋光線的實體具備幾何特徵且可以被檢測
雖然第三人稱2D遊戲本身也有多種視角,但為了簡單起見,在這裡先僅討論俯視視角的情況
參考文章:點擊跳轉
該文的翻譯版:點擊跳轉
該文章中描述了一種2D俯視角遊戲的FOV計算方式,其思考路徑和優化方案都是簡單易懂且能出效果的。
有了一般性的FOV計算思路,現在就可以進入2D瓦片式地圖的FOV討論了。
首先需要確定問題,即瓦片式地圖的FOV計算和普通2D的FOV計算有什麼不同。
如果遊戲並不需求FOV也基於地圖瓦片,即那種一格一格分開的離散態,那麼計算方式沒有什麼不同,無非就是在尋找周邊遮擋物的時候並不去考慮物體的幾何特徵或者碰撞體頂點,而是將地圖網格的格點當做頂點即可。
但若是希望FOV計算的結果基於瓦片,即得到一系列網格座標的集合而非多邊形組合,那麼算法就必須做出一些適當的修改了。
思路和前文提到的參考文章中類似,即並不考慮從玩家出發能看到哪些地塊,而是反向考慮哪些地塊能看到玩家,通過對特定範圍內的所有地塊都進行相同的計算後即可得到FOV的地塊集合。
具體過程大概描述如下:
  1. 找到一個待計算的FOV地塊集合,通常而言是曼哈頓距離小於等於視野半徑的所有地塊
  2. 遍歷該集合,取出每一個地塊計算其對玩家所在位置的可見性(即光線是否可達)
  3. 記錄下所有可見地塊到結果集合
不難看出,其中的重點在於如何針對視野範圍內的每個地塊,基於光線是否可達計算可見性。
不同於普通2D遊戲中可以通過射線檢測的方式來模擬光線,基於瓦片式地圖的檢測必須對光線進行網格擬合。
這個擬合過程大致描述如下:
1、通過起止點座標位置計算過兩點的直線斜率(即(y2-y1)/(x2-x1))
2、根據斜率的情況做分類討論
a) 如果斜率為零,表示這條直線水平,擬合時只需考慮X軸上的格點即可
b) 如果斜率為無窮大,表示該直線垂直,擬合時只需考慮Y軸上的格點
c) 如果斜率恰好為1,表示是一條標準對角線,擬合時考慮deltaX=deltaY的格點即可
d) 如果斜率為其它數值,表示該斜線沒有特點,那就採取通用的擬合方式
3、擬合的過程中如果檢測到某一格點被遮擋(即設定為阻擋視野),則中斷擬合,記錄檢測過的格點
4、循環結束後得到所有可視格點集合
其中所謂的通用擬合方式是指如下的計算方法:
1、如果斜率大於1則先設定Y座標為變化值,反之則設定X座標為變化值
2、將變化值朝原點(即視野計算起始點,玩家所在座標)位移一個單位(Y軸或者X軸其中之一)
3、計算另一軸的座標近似值(即將變化後的值帶入直線公式中計算結果並近似)
4、如果近似值和考察點的對應座標值之間的距離大於1,則改變變化值的設定(Y軸變為X軸或者反過來)
5、重複以上步驟直至考察點與原點重合或者進入已經確認的視野範圍或者遇到阻擋視野的格點
在這裡貼上部分通用擬合過程的代碼
var deltaX = point.x - origin.x; var deltaY = point.y - origin.y; var offsetX = deltaX < 0 ? 1 : -1; var offsetY = deltaY < 0 ? 1 : -1; if (changeX) { // X變化,Y要近似 iter.x += offsetX; var dy = iter.y - origin.y; var yValue = (deltaY * 1f / deltaX) * (iter.x - point.x) + point.y; var adaptY = dy > 0 ? Mathf.FloorToInt(yValue) : Mathf.CeilToInt(yValue); var nq = new Square(iter.x, adaptY); if (!checkVisibile(nq)) return false; yValue = (deltaY * 1f / deltaX) * (iter.x - point.x + offsetX) + point.y; if (Mathf.Abs(yValue - iter.y) >= 1f) changeX = false; } else { // Y變化,X要近似 iter.y += offsetY; var dx = iter.x - origin.x; var xValue = (deltaX * 1f / deltaY) * (iter.y - point.y) + point.x; var adaptX = dx > 0 ? Mathf.FloorToInt(xValue) : Mathf.CeilToInt(xValue); var nq = new Square(adaptX, iter.y); if (!checkVisibile(nq)) return false; xValue = (deltaX * 1f / deltaY) * (iter.y - point.y + offsetY) + point.x; if (Mathf.Abs(xValue - iter.x) >= 1f) changeX = true; }
全部格點擬合結束後即可找到符合要求的視野範圍
以上就是作者關於瓦片式地圖中的視野機制的粗淺認知和思考,作為遊戲中長久存在的重要機制,視野範圍的計算和應用必定有更深刻的理論和實踐存在,大家如有任何討論的想法都歡迎評論。
PS: 以上提到的視野機制在作者參與開發的遊戲《邊界迷航》中將有所體現,歡迎來看看或者關注一下~~
名稱:《邊界迷航》

© 2022 3樓貓 下載APP 站點地圖 廣告合作:asmrly666@gmail.com