問題? ? ? ??
????????前段一個同事在使用Unity開發時遇到一個奇怪的問題,使用左鍵點擊發射射線的方式選擇物體,總是選不準,尤其是小的物體,鼠標點擊到物體上,有時能選上,有時選不上,偶爾點擊到物體旁邊反而能選上,于是他讓我幫看看咋回事。我第一個想法是也許代碼寫的有問題吧,但我仔細檢查了同事寫的代碼,規規矩矩,完全沒問題。
????????就類似下面這種:
if (Input.GetMouseButtonDown(0))
{Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;if (Physics.Raycast(ray, out hit, 500)){Debug.Log(hit.transform.name);}
}
? ? ? ? 我一時也是有點兒發懵,但是當我檢查他的攝像機設置時,發現他把攝像機的近剪切平面距離設置的很小,是0.01,同事說是為了避免距離近的物體看不到,只能設置到最小,本來想設置為0,但是Unity不允許,最小就只能是0.01了。好吧,反正我也找不到問題所在,咱不妨把這個值先設置為默認值0.3,排除是這個引發問題的可能吧。結果不可思議的事情發生了,再次運行,馬上就能夠精確點擊了。
為啥(一)
? ? ? ? 問題是解決了,但是為什么呢?
????????先看看這行代碼:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
? ? ? ? 很多時候,這幾乎就是Unity的標準寫法,一直以來,我都沒有深入的研究過,我一直以為這個ray是從攝像機的位置發射出來,也就是ray.origin這個值應該和攝像機的位置是重疊的,但是,當我把ray.origin打印出來(代碼如下)的時候,我發現,這個這值位于攝像機的近剪切平面上,并不和攝像機位置重疊!!!
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.Log(ray.origin);
????????但是,這和近剪切距離的值有什么關系呢?
? ? ? ? 因為我們會發現,近剪切平面的長寬和近剪切距離是等比關系,如果近剪切距離小,那么意味著近剪切平面的長寬也變得很小,對于
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
這樣的代碼來說,射線的?ray.origin這個值的位置取決于鼠標在屏幕上的位置映射到近剪切平面的結果,而近剪切平面的長寬越大,越容易獲得一個精確的值(這個也實在是沒辦法的事情,Unity的位置精度是float嘛,而這種精度也不單單是Unity如此,大部分游戲引擎都如此,據說使用Double精度的位置對顯卡渲染速度影響很大,同時動力學計算也有問題,當然了,聽說而已,沒親身研究過)。
????????所以我們可以認為,如果近剪切距離是0.3的話,其精度是近剪切距離0.01的30倍,嗯,好像應該可以這么認為吧。
為啥(二)
? ? ? ? 但是為什么射線的起始點不是和攝像機位置重疊,偏偏要放在近剪切平面上呢?仔細想想,好像也很好理解,攝像機只會渲染其視錐體內的物體,不會渲染攝像機所在位置和近剪切平面之間的物體,如果射線的出發點和攝像機重疊,那么就可以點擊到攝像機所在位置和近剪切平面之間的物體,而這個物體用戶根本看不到!Unity當然不應該讓這種事情發生,于是把射線的位置放在了近剪切平面上就成了解決這個問題的絕佳方法!!
為啥啊
? ? ? ? 到此為止,我以為一切都在掌握之中,于是我認為,遠剪切平面之外的物體也點擊不了,不管你在Physics.Raycast方法中把射線發射距離設置為多遠!
? ? ? ? 然后我把Physics.Raycast方法中的maxDistance設置為2000,因為攝像機默認的遠剪切距離為1000,如果我把一個物體放置在遠剪切平面之外,那么一定是即渲染不了,也點擊不到。然而經過測試,發現并非如此,遠剪切平面之外的物體只要在射線發射的maxDistance距離之內,就能夠被點擊到,哈哈哈哈,為啥啊......