製作 oblique/cabinet 投影的 2D 遊戲時,確認精靈先後順序的排序方法
我經常說,我們以前玩過的很多遊戲雖然看上去是 2D 的,但其實是 3D 遊戲——比如熱血物語、雙截龍系列,它們具有一套三維的立體空間邏輯,從而不再使得遊戲的舞臺限制在只有左右移動和上下跳躍這兩種軸向——擁有三個維度極為自由開放的移動範圍相信也給很多玩家留下了深刻的印象,在上次的文章裡,我們已經介紹了這種遊戲的物理部分簡單實現,本次將會聊到這類遊戲在渲染上的重要技巧。
顯然,在用 2D 精靈模擬的 3D 空間裡,任何一個物體都不簡單:如圖所示的一個立方體,具有一個 oblique 視角(或者,我們叫 cabinet 視角更準確)的造型。這樣的視角下,如果有另外一個精靈和它相互遮擋,應該怎麼處理它們的渲染順序關係呢?
注:為了簡化描述,本文章的 3D 空間假設所有物件的 z 座標都相同,即“沒有和地面的高度差”。
任意兩個物體之間的排序
按照我們一般的簡單思路(比如在經典的 topdown 視角下,如 RPGMAKER 系列),誰在世界上具有較大的 y 值,誰就靠近螢幕,誰就應該更靠後地渲染,對吧?不過在這樣的視角下,就沒有那麼簡單了,來看一個例子:
如圖,根據十字形標記,我們可以輕鬆地發現 shari 的 y 座標小於立方體的座標,所以她理應被擋在立方體的後面(先於立方體渲染),對吧?可是當她來到另外一側的時候,情況就變化了:
如圖,shari 的 y 座標仍然小於立方體的 y 座標,但這個位置上,我們是希望 shari 遮擋立方體而不是被立方體遮擋的,所以我們需要考慮到更多的情況。
顯然,這個世界裡的任何一個物體都應該攜帶它的"底座"資訊。如圖,紅色的平行四邊形就是上面立方體的"底座"資訊,而綠色是它的高度資訊(如果你需要一些更細緻的排序,比如算上 z 軸,本文暫不討論)有了這個資訊,我們就可以確定任意兩個物件之間的繪製順序。
我們把兩個要排序的物體分別命名為 A 和 B,A 底座的上邊緣稱為 aTop,下邊緣稱為 aBottom;B 底座的上邊緣稱為 bTop,下邊緣稱為 bBottom。
aBottom 的 y 座標小於 bTop 的 y 座標:
這時候 A 顯然是在 B 的後方,也就是 A 先於 B 渲染。
aTop 的 y 座標大於 bBottom 的y 座標:
這時候 A 顯然是在 B 的前方,也就是 B 先於 A 渲染。
其它的情況:
由於我們這裡的視角下,側面是一個 45 度的斜線,所以我們可以很輕鬆地把兩個物體下邊緣的 y 軸距離加到 aBottom 的右端點去進行判斷,像是這樣:
distanceY = bBottom.y - aBottom.y
aBottom.left.x > bBottom.right.x + distanceY
如果這個判斷是成立的,那就是上圖所述的情況:A 在 B 的前方,也就是 B 先於 A 渲染。
否則就是下圖所述的情況:A 在 B 的後方,也就是 A 先於 B 渲染。
對場景內所有物體的排序
好,至此我們已經可以為場景內任意的兩個物件進行排序了,那麼,我們也可以為場景中所有的物件組織排序。很多人的第一反應是,直接用冒泡/選擇/歸併/快排這樣的演算法來對場景中需要排序的物體進行排序不就好了嗎?其實這是不合適的,因為場景中的物體遮擋存在拓撲關係,如果直接使用線性表的排序方法,可能在 swap 一對物體以確保他們遮擋關係的同時又會直接破壞了另一對物體的遮擋關係,因此,我們的第一步是對場景中需要排序的物體進行連線。
圖片帶有弧線的那一端是射線的末端,末端的物體比首端的物體更先渲染(位置更靠後)。
這裡說明一下,我們只對需要排序的一對物體進行連線(出於效能考慮),所以你的顯示物件可能需要有相應的 bounding_box 或是什麼的。
當場景中的物體足夠多,遮擋關係足夠複雜時,不難發現,我們的物體遮擋關係實際上構建出了一個有向圖。而有向圖可以通過拓撲排序來獲取一個不唯一的線性序列,照著這個線性序列進行渲染就可以獲得正確的場景排序了。
(圖中藍綠色的矩形是判斷精靈相交/重疊用的 bounding_box,DRAWIDX 則是拓撲排序後得到的渲染順序值)
這樣,就得到了整個場景的正確呈現順序。
最後一點問題
相信我們都看到過一些"視覺錯覺"的趣味圖片,對吧?譬如三個無限迴圈的臺階,相互遮擋的三個稜柱……之類的,很有意思,不過在遊戲開發當中可一點也不有趣。很顯然,當遊戲場景中出現了幾個互相存在遮擋和被遮擋關係的物件時,我們構造出的有向圖就成環了。而成環的有向圖是不能進行拓撲排序的,這就導致我們沒有辦法去按正確順序呈現畫面。解決這個問題的最好方法就是把容易引起該問題的那個物體分割為兩個物體,使得其中的一個部分"專門遮擋"而另一個部分"專門被遮擋",這樣就可以消除我們構造的有向圖中的環。
本次的討論就到這裡,祝大家開發順利,新年快樂!
作者:lanza
來源:indienova
地址:https://mp.weixin.qq.com/s/-8-cXkJ9hG8Di-yeJcPo7w
相關文章
- 【PB】事件的觸發時機及先後順序事件
- Oracle確定連線方式的優先順序Oracle
- 如何確定DevOps變更的優先順序?dev
- 任務卡片優先順序排序-Leangoo排序Go
- 快速掌握RabbitMQ(三)——訊息確認、持久化、優先順序的C#實現MQ持久化C#
- Unity 2D遊戲製作Unity遊戲
- 順序表的堆排序排序
- 等級+時間的優先順序演算法演算法
- sqlserver使用order by case when進行優先順序排序SQLServer排序
- python if語句有先後順序嗎Python
- [譯]HTTP/2的優先順序HTTP
- CSS的處理優先順序CSS
- CSS優先順序CSS
- python 精靈模組非常簡單的讓小朋友製作遊戲和動畫Python遊戲動畫
- 如何確定六西格瑪專案改善課題的優先順序?
- SAP MM 系統確定供應源優先順序
- 《心靈願望》技術總監:大幅縮短 2D 遊戲角色製作時間的自動化技術遊戲
- python運算子及優先順序順序Python
- css選擇器的優先順序CSS
- 談Nginx的Location匹配優先順序Nginx
- 警惕執行緒的優先順序執行緒
- 併發請求的優先順序
- CSS 選擇器的優先順序CSS
- nginx的location匹配順序、優先順序,location對映衝突排查Nginx
- NTP時間伺服器優先順序配置伺服器
- 二叉樹的四種遍歷方法:先序,中序,後序,層序二叉樹
- laravel Route RESTful 因路由先後順序導致的解析錯誤LaravelREST路由
- 中斷優先順序
- 讓 排序 按照 in 列表的的顯示順序排序輸出。排序
- mybatis配置檔案(其中,注意節點先後順序)MyBatis
- 遊戲音樂的認識與製作三遊戲
- 使用360安全衛士設定軟體優先順序的方法
- Java之執行緒的優先順序Java執行緒
- HttpClient和HttpGet 引數的優先順序HTTPclient
- Nginx location 在配置中的優先順序Nginx
- C++操作符的優先順序C++
- 【分享】如何評估 bug 的優先順序
- SAP ABAP 報表幾個事件的先後執行順序和作用事件