坐標系和坐標系轉換
來自專欄遊戲開發入門指南——Unity+47 人贊了文章
大家好。今天帶來的是一個講座性質的內容。這裡的概念搞清楚且能純熟運用了,對於遊戲開發那是相當的有用。
首先來介紹一下我們經常會用到的幾種坐標系:
一.世界坐標系
遊戲場景中所有的物體都統一遵守的坐標系統,它標註了每個物體在世界中的唯一位置方向信息。
如上圖所示,我們用一個平面世界坐標系來表示紅點和藍點的位置,往後無論增加多少個點,都可以在該坐標系中找到一個數值來表示位置,只要被標註位置的物體不動,位置數值永遠不變。
所以世界坐標系又叫絕對坐標系,我們在Unity中使用的世界坐標不過是多了一根Z軸的空間坐標系罷了,原理是一樣的。
二.本地坐標系
本地坐標系則是以物體自身位置作為原點,表示物體間相對位置和方向,並且會根據物體自身旋轉而旋轉:
我們在Inspector看到的坐標系其實就是本地坐標系,包括旋轉、縮放都屬於本地:
當一個物體有父物體時,無論父物體的位置旋轉縮放如何變化,作為子物體它的Transform數值始終保持不變,說明它相對於自己的父物體位置始終沒有變化,而當一個物體沒有父物體時,可以把「世界」 就看成它的父物體,它在世界中的一切位置變化都可以看做是和世界的相對位置變化,由於世界坐標系和「世界的本地坐標系」是重合的,所以該物體的本地坐標和世界坐標也是一樣的。
由於物體各自坐標系的不同參考標準,同一個物體,會在不同本地坐標系中顯示出不同的位置信息,產生各種各樣的歧義:
熊大將美女在本地坐標系的方向位置告訴熊二,熊二如果也往左手邊這個方向看是肯定看不見美女的,所以本地坐標系如果使用不當很容易帶來混亂。而如果熊大能將美女的世界坐標告訴熊二,熊二就不會感到迷惑了。
三.屏幕坐標系
在Unity的Game視窗中,以屏幕左下角為原點(0,0),像素為單位,坐標軸往屏幕右上方進行延伸,X軸和Y軸數值不能超過屏幕的最大寬度和最大高度。由於原點位置是唯一的,也可以理解成是一個平面世界坐標系,作用是表示物體在屏幕中的位置。
四.視口坐標系
視口就是當你在Unity 的Scene面板中看到攝像機的那個白色矩形框:
視口坐標系同屏幕坐標系相似,也是以左下角為原點的平面坐標系,不同的是視口坐標系的數值上限不受屏幕大小影響,右上角的坐標永遠是(1,1),數值就是物體屏幕坐標與屏幕寬高的比例:( X軸/寬度, Y軸/高度),作用是表示物體在攝像機中的位置。
對這幾種坐標系有一個基本的了解後,我們就來看坐標系之間的轉換:
一.本地坐標<==>世界坐標:
●本地轉世界:transform.TransformPoint
用一個案例來表示:
1.在場景中有物體A和B,且B是A的子物體:
2.在Inspector面板將A的坐標設為(0,0,5),B的坐標設為(0,0,2),那麼這時B的世界坐標就應該是(0,0,7)。
3.用代碼來驗證我們的推斷:
public Transform p; //A物體public Transform s; //B物體void Update(){ if (Input.GetKeyDown(KeyCode.A)) //按下A鍵 { print("子物體的本地坐標: " + s.localPosition); print("子物體的世界坐標: " + s.position); print("本地轉世界: " + p.TransformPoint(s.localPosition)); }}
4.運行遊戲,點擊A鍵:
●本地轉世界:transform.InverseTransformPoint
將剛才的代碼稍稍改一下:
if (Input.GetKeyDown(KeyCode.A)) //按下A鍵{ print("子物體的本地坐標: " + s.localPosition); print("子物體的世界坐標: " + s.position); print("世界轉本地: " + p.InverseTransformPoint(s.position));}
二.世界坐標<==>屏幕坐標:
●世界轉屏幕:Camera.main.WorldToScreenPoint
屏幕坐標是一個二維坐標(X,Y),所有被攝像機照的的三維物體都可以在這個二維屏幕上找到自己的點:
在遊戲遊戲中我們可以看到這種現象:
顯示在人物身上的名字信息,會始終跟著人物移動,並且無論人物離攝像機多遠多近,字體大小始終一樣(通過兩幅圖主角血條和頂上的時間顯示可以看出兩個屏幕是一樣大小),其實就是將人物的世界坐標轉成屏幕坐標,再將這個坐標賦給顯示名字的UI,接下來我們就通過一個實例來完成:
1.在場景中創建一個Cube,再用UGUI創建一個Text,內容改為「小兵」:
2.創建腳本:
public Transform cube; //將Cube拖進去public Text text; //將Text拖進去void Update(){ //獲取Cube頂上的位置的世界坐標 Vector3 pos = cube.position + Vector3.up; //將該世界坐標轉為屏幕坐標賦給Text text.transform.position = Camera.main.WorldToScreenPoint(pos);}
3.運行遊戲:
●屏幕轉世界:Camera.main.ScreenToWoldPoint
和世界轉屏幕相反,屏幕轉世界則是,將屏幕坐標的XY軸信息轉換後再增加一個Z軸信息,賦給三維世界的物體:
滑鼠坐標也是屏幕坐標,我們就來做一個三維物體跟隨滑鼠移動的案例:
1.創建一個Cube,讓攝像機正對著它:
2.創建腳本:
public Transform cube; //方塊public Transform mainCamera; //主攝像機void Update(){ if (Input.GetMouseButton(0)) //按住滑鼠左鍵 { //獲取滑鼠屏幕坐標(Z軸為0) Vector3 mPos = Input.mousePosition; //以攝像機z軸為法線創建攝像機XY軸組成的平面 Plane pla = new Plane(mainCamera.forward, mainCamera.position); //獲取該物體到平面的距離(z軸垂直距離) float dis = pla.GetDistanceToPoint(cube.position); //將屏幕坐標轉為世界坐標 cube.position = Camera.main.ScreenToWorldPoint(new Vector3(mPos.x, mPos.y, dis)); }}
3.運行程序:
四.屏幕坐標<==>視口坐標:
●屏幕轉視口:Camera.main.ScreenToViewportPoint
上面我們說到視口坐標實際就是屏幕坐標與屏幕的寬高比,我們通過一個案例來進行驗證:
1.場景中用UGUI新建兩個Text,分別用於顯示視口坐標與比例:
2.我們還是使用滑鼠的屏幕坐標來進行轉換,同時將滑鼠在攝像機的位置用比例表示出來,創建腳本:
public Text text; //顯示視口坐標 public Text text1; //顯示比例 void Update() { //獲取滑鼠坐標的屏幕坐標 Vector2 mouPos = Input.mousePosition; //將屏幕坐標轉為視口坐標 Vector2 viewPos = Camera.main.ScreenToViewportPoint(mouPos); //顯示視口坐標(保留小數點後兩位) text.text = "視口: " + viewPos.ToString("0.00"); //獲取屏幕坐標與屏幕的寬高比 float x = mouPos.x / Screen.width; float y = mouPos.y / Screen.height; Vector2 viewpos1 = new Vector2(x, y); //顯示比例 text1.text = "比值: " + viewpos1.ToString("0.00"); }
3.運行程序:
●視口轉屏幕:Camera.main. ViewportPointToScreenPoint
public Text text; //顯示視口坐標public Text text1; //顯示比例void Update(){ //獲取滑鼠坐標的屏幕坐標 Vector2 mouPos = Input.mousePosition; //直接顯示屏幕坐標 text.text = "直接顯示屏幕坐標: " + mouPos.ToString(); //獲取屏幕坐標與屏幕的寬高比 float x = mouPos.x / Screen.width; float y = mouPos.y / Screen.height; Vector2 viewpos = new Vector2(x, y); //將視口坐標轉為屏幕坐標 Vector2 mouPos1 = Camera.main.ViewportToScreenPoint(viewpos); text1.text = "轉換後的屏幕坐標: " + mouPos1.ToString();}
總結:
Unity中的坐標系當然不止上述四種,除此之外如投影坐標系、切線坐標系、GUI坐標系等,坐標轉換除了點的轉換外,還有方向和向量的轉換,如果有興趣可以查看Unity官方文檔了解。
坐標系轉換在遊戲中用得非常廣泛,學習過程中只要能夠弄懂坐標系的定義,再去理解它們之間的轉換,就相對容易許多。
來,想系統性學習遊戲開發、學習Unity開發的,歡迎圍觀:
http://levelpp.com/
在線的教學視頻:
https://space.bilibili.com/38043731/#/
以及QQ交流群:869551769
推薦閱讀: