我們?cè)谠O(shè)計(jì)游戲的時(shí)候乞旦,經(jīng)常會(huì)進(jìn)行坐標(biāo)系的變換贼穆,Unity為我們提供了多個(gè)變換的API,這里主要對(duì)它們的使用做一個(gè)總結(jié)整理兰粉!
在Unity中我們通常會(huì)用到以下幾個(gè)坐標(biāo)系下的點(diǎn):
世界坐標(biāo)系故痊、觀察坐標(biāo)系、ViewPort玖姑、屏幕坐標(biāo)系
-
世界坐標(biāo)系:World Space
簡(jiǎn)單來(lái)講愕秫,我們通過(guò)transform.position | transform.rotattion
獲取得到的位置和旋轉(zhuǎn)信息都是基于世界坐標(biāo)系的,可以說(shuō)焰络,我們的很大一部分操作都是基于世界坐標(biāo)系戴甩。 -
觀察坐標(biāo):Eye Space
我們?cè)赨nity的Game視圖中觀察的畫(huà)面始終是由攝像機(jī)提供的,基于攝像機(jī)的一個(gè)坐標(biāo)系也就是"Eye Space"(簡(jiǎn)單來(lái)講就是把攝像機(jī)看作原點(diǎn)位置)闪彼。 -
視口坐標(biāo):View Port
視口是針對(duì)游戲顯示的畫(huà)面進(jìn)行描述的甜孤,View Port用于描述整個(gè)游戲畫(huà)面的坐標(biāo),左下角為(0,0)
,右上角為(1,1)
畏腕,我們?cè)谠O(shè)計(jì)分屏游戲的時(shí)候可以通過(guò)設(shè)置攝像機(jī)所占據(jù)的視口空間來(lái)控制缴川。 -
屏幕坐標(biāo):Screen Space
屏幕坐標(biāo)開(kāi)始和像素扯上關(guān)系了,也就是說(shuō)屏幕坐標(biāo)和分辨率有關(guān)描馅,屏幕的左下角為(0,0)
,但右上角為(screen.width,screen.height)
把夸。比如游戲的分辨率為500*600
,則screen.width=500;screen.height=600
铭污。
這里需要說(shuō)明的是恋日,我們?cè)讷@取鼠標(biāo)位置的時(shí)候膀篮,
Input.mousePosition
來(lái)獲取鼠標(biāo)的位置,這里獲取到的鼠標(biāo)位置是基于屏幕坐標(biāo)的谚鄙。通過(guò)該函數(shù)返回的是Vector3
類(lèi)型的變量各拷,但z
分量始終為0。讀者可以自行進(jìn)行嘗試闷营。
這里,我們先來(lái)看一下Unity提供的相關(guān)常見(jiàn)函數(shù):
//1.屏幕轉(zhuǎn)世界坐標(biāo)
Vector3 Camera.main.ScreenToWorldPoint(new Vector3(screenPos.x , screenPos.y , zInfo));
//2.世界轉(zhuǎn)屏幕坐標(biāo)
Vector3 Camera.main.WorldToScreenPoint(new Vector3(worldPos.x , worldPos.y , worldPos.z));
//3.世界轉(zhuǎn)視口坐標(biāo)
Vector3 Camera.main.WorldToViewportPoint();
//4.視口轉(zhuǎn)世界坐標(biāo)
Vector3 Camera.main.ViewportToWorldPoint(new Vector3(viewPortPos.x , viewPortPos.y , zInfo));
//5.視口轉(zhuǎn)屏幕坐標(biāo)
Vector3 Camera.main.ViewportToScreenPoint();
//6.屏幕轉(zhuǎn)視口坐標(biāo)
Vector3 Camera.main.ScreenToViewportPoint();
作者作為初學(xué)者的一員知市,認(rèn)為先搞清楚這幾個(gè)暫時(shí)足夠傻盟,日后若有使用更多的變換,則再進(jìn)行補(bǔ)充吧嫂丙。
觀察這些個(gè)函數(shù)娘赴,首先一個(gè)很明顯的共同點(diǎn),就是這些函數(shù)都是Camera
的成員函數(shù)跟啤,輸入和輸出都為Vector3
類(lèi)型的變量诽表。也即這些函數(shù)都是針對(duì)當(dāng)前攝像機(jī)的一個(gè)變換操作。這很容易理解隅肥,因?yàn)?D游戲中的坐標(biāo)從模型空間到最終的屏幕空間經(jīng)過(guò)了model
,view
,projection
,以及之后的NDC變換
等竿奏,其中除model
是用于從模型空間到世界空間的變換外,之后的view
腥放,projection
都是基于攝像機(jī)的泛啸。他們會(huì)隨著使用相機(jī)的變化而變化。至于具體的內(nèi)容秃症,變化過(guò)程候址,這里不做過(guò)多描述,讀者可以查看網(wǎng)上的相關(guān)文章种柑。
當(dāng)然岗仑,我們?cè)谑褂眠@些API的時(shí)候,只需要清楚我們的輸入和輸出的內(nèi)容及其關(guān)系就好了聚请。
接下來(lái)荠雕,我們來(lái)聊一聊這些函數(shù):
首先是屏幕坐標(biāo)和世界坐標(biāo)的相互轉(zhuǎn)換:
WorldToScreenPoint函數(shù)接收一個(gè)世界空間下的位置信息,然后返回其所在的屏幕空間位置良漱,以及其相對(duì)于攝像機(jī)的深度信息舞虱,該深度信息由世界空間下攝像機(jī)和輸入位置的z值來(lái)決定。
一個(gè)例子是:攝像機(jī)的位置為(0.0, 0.0 , -10.0),輸入的位置為(0.0,0.0,1.0)母市。則返回的結(jié)果為(screen.width/2 , screen.height/2 , 1-(-10));
注意攝像機(jī)指向-z方向矾兜!
ScreenToWorldPoint則是與之相反,輸入屏幕空間位置以及相應(yīng)的深度信息(注意深度信息應(yīng)該為目標(biāo)z值金和相機(jī)z值的差值)患久,可以返回其所在的世界坐標(biāo)位置椅寺。
視口坐標(biāo)和世界坐標(biāo)與之相似:
WorldToViewportPoint:輸入世界坐標(biāo)浑槽,返回的是對(duì)應(yīng)的點(diǎn)所在的視口位置,當(dāng)然以及其相對(duì)于攝像機(jī)的深度信息(距離)
ViewportToWorldPoint:輸入視口坐標(biāo)(記得對(duì)應(yīng)的深度信息)返帕,返回點(diǎn)所在的世界坐標(biāo)
視口坐標(biāo)和屏幕坐標(biāo)非常簡(jiǎn)單桐玻,正如上面的說(shuō)明,只要知道分辨率就可以輕松轉(zhuǎn)換荆萤,這里不再贅述镊靴。
一個(gè)簡(jiǎn)單的應(yīng)用
我們?cè)谠O(shè)計(jì)某些游戲的時(shí)候(比如攝像機(jī)固定不動(dòng)的類(lèi)型),會(huì)對(duì)物體的運(yùn)動(dòng)范圍進(jìn)行限制链韭,以防止其跑出邊界偏竟。比如一盒橫屏的飛行射擊游戲(雷電),我們可以獲得物體的位置信息:transform.position
敞峭,我們希望對(duì)物體的x
,y
兩個(gè)軸向的移動(dòng)進(jìn)行限制(同時(shí)凍結(jié)物體的z軸移動(dòng))踊谋。一個(gè)簡(jiǎn)單的思路如下:
public float leftBorder;
public float rightBorder;
public float topBorder;
public float bottomBorder;
....
Vector3 leftBtm_cornerPos = Camera.main.ViewportToWorldPoint(new Vector3(0f, 0f,
Mathf.Abs(-Camera.main.transform.position.z))); //這里的z軸在正交視圖下意義不大
Vector3 rightTop_cornerPos = Camera.main.ViewportToWorldPoint(new Vector3(1f, 1f,
Mathf.Abs(-Camera.main.transform.position.z)));
....
leftBorder = leftBtm_cornerPos.x;
rightBorder = rightTop_cornerPos.x;
topBorder = rightTop_cornerPos.y;
bottomBorder = leftBtm_cornerPos.y;
....
if (pos.x <= leftBorder)
{
pos.x = leftBorder;
}
else if (pos.x >= rightBorde
{
pos.x = rightBorder;
}
if (pos.y <= bottomBorder)
{
pos.y = bottomBorder;
}
else if (pos.y >= topBorder)
{
pos.y = topBorder;
}
上面的代碼通過(guò)ViewportToWorldPoint
獲取到了四個(gè)邊界。之后通過(guò)限制位置的x
旋讹、y
軸向移動(dòng)就可以控制避免移動(dòng)過(guò)度殖蚕。
說(shuō)明一下,這里假設(shè)了雷電類(lèi)型的游戲沉迹,使用的攝像機(jī)為正交類(lèi)型的投影方式睦疫,了解正交投影的朋友就知道,視錐體變成了長(zhǎng)方體形胚股,因子這里的
z
軸并無(wú)太大的作用笼痛。即使我使用ViewportToWorldPoint
的時(shí)候使用0
作為z
軸數(shù)據(jù),也一樣沒(méi)關(guān)系琅拌。但是對(duì)于透視投影就不太一樣了缨伊。若這里使用的透視投影,在不同的深度下进宝,其的邊界范圍肯定也會(huì)變化刻坊,這個(gè)時(shí)候就必須輸入正確的z
軸數(shù)據(jù)了!党晋。