我目前正在 Unity 中撰寫一個 Minecraft 克隆作為我的寵物專案,并且我正在嘗試為其實作光線投射,以便我可以知道玩家正在查看哪個塊(我也會將光線投射用于其他目的)。游戲的世界是一個完美單位立方體的 3D 網格。網格的每個元素要么是實心的,要么不是實心的。我希望能夠從我的世界的任何一點射出射線,并獲得射線以它的方式擊中第一個固體塊表面的點。
這是我的游戲大致看起來的 ac# 偽代碼:
// An aproximation of what Unity's Vector3 looks like.
public struct Vector3
{
public float x, y, z;
}
public class World
{
public bool[,,] blocks;
public bool IsSolid(Vector3 pos) // i.e. if pos is inside a solid block
{
return blocks[Math.Floor(pos.x), Math.Floor(pos.y), Math.Floor(pos.z)]
}
public Vector3 Raycast(Vector3 origin, Vector3 direction)
{
// some algorithm, that returns the point at which ray hits a solid block
}
}
請注意,any 的坐標Vector3可能不是整數,光線完全有可能在分數坐標處開始(或結束)。為簡單起見,您可以(或不可以)假設世界是無限的,并且射線最終總會擊中某個塊。請記住,這Raycast()應該回傳光線撞擊立方體表面的點。
我可以為此使用的最佳演算法是什么?我的優先事項(按順序)是:
- 速度- 制作光線投射應該很快
- 優雅——演算法應該相當簡單明了
- 通用性——演算法應該易于修改(即添加一些額外的功能。)
這是一些可能的問題的問答:
問:為什么不使用 Unity 的原生碰撞器和光線投射?
答: Unity 的碰撞器和光線投射太慢了,沒有針對我的需求進行優化,此外,這絕不是通用的優雅。
問:你想要一個演算法的實作還是只是基本概念?
答:我只是了解演算法的基礎就可以了,但我真的很欣賞實作(最好是在 C# 中)。
uj5u.com熱心網友回復:
以下是一些基本原則。你需要對線性代數相當熟悉才能嘗試這個,并閱讀和理解射線平面相交的作業原理。
您的光線將從立方體內部開始,它會在離開立方體的途中擊中 6 個面中的一個。在正常情況下,我們可以通過檢查光線方向是否與立方體面指向相同的方向來快速消除三個面。這是通過檢查向量之間的點積的符號來完成的。為了找到剩下的三個面中的第一個命中,我們對相應的平面進行了相交測驗,并選擇最接近的一個,如果你知道命中的面,你就知道它擊中了哪個立方體。如果那個立方體是空的,你重復這個程序,直到你找到一個非空的立方體。您還可以添加一些檢查以避免邊緣情況,例如盡早消除與光線平行的所有平面。
但是,要獲得任何真正的速度,您確實需要某種樹結構來減少完成的檢查次數。有很多替代方案,kd-trees,r-trees 等,但在這種特定情況下,我可能會考慮使用sparse octree。這意味著您的立方體將成為較大的 2x2x2 部分的一部分,其中整個部分可以標記為空、填充、部分填充等。這些部分也將被分組為更大的 2x2x2 部分等等,直到您達到某個最大尺寸包含您的整個游樂區,或您游樂區的一個可獨立加載的單元,并在需要時有一些邏輯來測驗多個單元。
Raycasting 使用八叉樹的方式或多或少與簡單情況相同,只是您現在擁有可變大小的立方體。當你碰到一張臉時,你需要遍歷樹來找到下一個要測驗的立方體。
但要真正實作這一點的優化,需要相當多的經驗,無論是在涉及的概念方面,還是在語言和硬體方面。您可能還需要深入了解實際游戲世界的結構,因為可能有一些不明顯的捷徑可以幫助您提高性能。所以不能保證你的實作會比內置的統一更快。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/524609.html
