技术分享:游戏中的 2D 可见性
2D的俯視圖經(jīng)常用于從給定點(diǎn)計(jì)算可視區(qū)域。例如,你可能想把某些東西隱藏在玩家看不見的地方,亦或你想知道點(diǎn)燃火炬后能看見什么地方。
?
這個(gè)算法也能計(jì)算出給定光源所照亮的區(qū)域。對(duì)每條光線,我們可以構(gòu)建出被照亮區(qū)域的光線圖。如果我們給上面的迷宮放上24個(gè)燈呢?見光線圖。
roguelike(譯注:類地下城RPG游戲統(tǒng)稱)社區(qū)已經(jīng)收集了好幾種算法,尤其是網(wǎng)格類的。消減算法是從可見的一切區(qū)域開始,減去不可見區(qū)域;添加算法是從不可見區(qū)域開始,加上可見區(qū)域。我將描述一種可工作于線段的添加算法,不僅僅是固體分塊或者網(wǎng)格。
光線投射
一個(gè)簡單地方法是從中心點(diǎn)投射光線,這是得到一個(gè)近似解的合理的第一步:
? 更聰明的是,讓光線投射到所有墻體的開端和末端。這些光線所產(chǎn)生的三角形就是可見區(qū)域:
? 就是這樣!該算法如下:
計(jì)算到墻體開始或結(jié)束的角度。
從中心點(diǎn)沿各個(gè)角度投出光線。
對(duì)這些光線所產(chǎn)生的三角形進(jìn)行填充。
墻體跟蹤
我們可以到此為止了,尤其是如果我們有一個(gè)快速光線投射算法,可使用空間哈希以避免與每一個(gè)墻體進(jìn)行相交計(jì)算。然而,更有效的方法是將光線投射和墻體相交結(jié)合成一個(gè)算法。我將在這里描述了一種圓形掃描算法,對(duì)所有的擊中點(diǎn)按角度進(jìn)行排序; 它也可以擴(kuò)展成圓形外擴(kuò)算法,對(duì)所有的擊中點(diǎn)按半徑排序,但我還沒有嘗試過這種方法。
位于連續(xù)幾個(gè)射線之間的區(qū)域,我們需要找到最近的墻。這面墻就被照亮了; 所有其他墻面應(yīng)該被隱藏。我們的策略是360°掃描,處理所有的墻端點(diǎn)。當(dāng)運(yùn)行時(shí),我們會(huì)持續(xù)跟蹤與掃描線相交的墻壁。點(diǎn)擊觀看端點(diǎn)掃描:
? 下一步驟是將跟蹤哪些墻壁會(huì)被掃描線穿過。只有最近的壁是可見的。你如何找出哪些墻壁是最近的?最簡單的方法是計(jì)算從中心到墻的距離。然而,如果墻壁大小不同,這種方法不能很好地工作,所以演示中使用一個(gè)稍微復(fù)雜的方法,這里我就不解釋了。
按PLAY可看到掃描中最近的墻面以白色繪制和其他墻面繪成黑色。
? 每當(dāng)最近的墻面終止,或者有新的墻面比其它的都近時(shí),我們創(chuàng)建了一個(gè)三角形表示可見區(qū)域。這些三角形的并集就是所述中心點(diǎn)的可視區(qū)域。
00000
需要注意的是創(chuàng)建一個(gè)三角形涉及到之前與掃描線相交的墻面。其結(jié)果是,三角形的新邊緣可能長于或短于掃描線,并且該三角形最遠(yuǎn)的邊緣比之前的墻面短。
試驗(yàn)場
這里有一塊試驗(yàn)場,有很多可用的方塊。可以拖拽方塊到網(wǎng)格內(nèi)。點(diǎn)擊play/pause按鈕可以查看算法運(yùn)行,或者移動(dòng)中心點(diǎn)查看哪些是可見的,就像玩家四處查看一樣。
? 組合輸出
我們可以使用集合運(yùn)算以有趣的方式組合該算法的輸出。手機(jī)號(hào)拍賣平臺(tái)這些也可被實(shí)現(xiàn)為用布爾運(yùn)算分析輸出,或者用位圖操作渲染輸出。 玩家視野
限制玩家的視野最簡單的操作是將輸出與有限的視野求交集。例如,相交算法使用圓圈來限制可見半徑。與漸變填充圈相交,可使光按距離改變明暗。與圓錐相交可打造出“手電筒”效果,可以讓你把前面看得更遠(yuǎn),但沒有相應(yīng)視野在你身后(見隨后Dynamite Jack的一個(gè)例子)。假如用雙眼代替單點(diǎn),玩家的視野也更好看。我希望你可以合并所有眼睛的可視區(qū)域,但我還沒有試過。
地圖物體
可見性也可用于計(jì)算哪些區(qū)域被火炬點(diǎn)亮。在頁面的頂部演示了首先對(duì)每個(gè)火炬所點(diǎn)亮的區(qū)域進(jìn)行求并集,然后與玩家可以看見的區(qū)域相交。(請(qǐng)注意,此算法會(huì)產(chǎn)生硬陰影,你將不得不對(duì)輸出進(jìn)行后處理來獲得軟陰影。)
同樣的計(jì)算可用于確定哪些地區(qū)可被安全攝像頭可以看到,有哪些被盾牌保護(hù)著,或者是否足夠靠近某些魔法設(shè)施,使它賦予你屬性加成或是詛咒。
AI行為
可見性也可用于構(gòu)建AI行為。例如,假設(shè)敵人的AI是想扔了一枚手榴彈擊中玩家,也想站在玩家射擊不到的地方。手榴彈需要足夠近才能擊中玩家,并且無法擊中障礙物后面的。下圖顯示標(biāo)注了AI單位的地圖的可能計(jì)算:
? 手榴彈扔進(jìn)紫色區(qū)域?qū)⒊晒糁幸幻婕摇|S色和紫色區(qū)域是危險(xiǎn)區(qū)域; 玩家可以從那里攻擊AI單位。AI需要站在一個(gè)安全的區(qū)域(深藍(lán)色)并且投擲了一枚手榴彈到紫色區(qū)域,然后尋找掩體。如何計(jì)算掩體?在AI準(zhǔn)備投擲手雷的地方再次運(yùn)行可見性算法,讓櫥柜和桌子擋住視線。
實(shí)現(xiàn)
我已經(jīng)用HAXE 3來實(shí)現(xiàn)這個(gè)算法,使用Apache v2開源協(xié)議(類似MIT和BSD,它可以在商業(yè)項(xiàng)目中使用)。HAXE代碼可以編譯成JavaScript,ActionScript,C ++,Java,C#或PHP。我把它編譯成JavaScript來制作這個(gè)網(wǎng)頁,并為我的其他項(xiàng)目編譯成Flash。我編譯成以下語言:
?
- Actionscript;可讀,因?yàn)锳ctionscript和Haxe并非截然不同
- Javascript(用于此頁面上的演示);大多是可讀的。
- Java ;輕度可讀,但不是很好。
- C# ;輕度可讀,但不是很好。Roy Triesscheijn有一個(gè)更好的版本在這里。
Wade Tritschler建議手工移植,所產(chǎn)生的代碼要比使用Haxe輸出的代碼更干凈。我同意這個(gè)觀點(diǎn)。如果你手寫代碼還可以更好得了解該算法。盡管該算法主要在CPU中進(jìn)行,可以使用GPU為位圖進(jìn)行三角形渲染和合并位圖輸出。(布爾AND操作可變成位圖乘法;布爾OR操作可變成位圖添加和鉗位。)在我的項(xiàng)目中該性能已經(jīng)足夠,所以我還沒有構(gòu)建GPU版本。如果你的游戲有CPU限制,可以考慮使用消減算法(而不是這里顯示的添加算法),渲染四邊形的每條線段的影子。它會(huì)增加GPU渲染負(fù)載,但它并不需要在CPU上排序。如果填充率是一個(gè)問題,考慮渲染一個(gè)比游戲畫面分辨率低的光度圖,然后擴(kuò)大它。
相關(guān)內(nèi)容
視覺和光線覆蓋了可見性的問題; 在我的博客文章有更多的鏈接。地平線問題類似2D可見性問題,但它在直角坐標(biāo)系中,而不是極坐標(biāo)。另外還有美術(shù)館問題,關(guān)于放置多少個(gè)警衛(wèi)就可以看到地圖的每一個(gè)區(qū)域。我正Trello上創(chuàng)建了一份列表,未來可能更新這個(gè)頁面。
總結(jié)
以上是生活随笔為你收集整理的技术分享:游戏中的 2D 可见性的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 开发笔记:掉落系统模块设计思路
- 下一篇: 移动平台游戏网络重连方案