這次就是把UGUI中的Local物件座標跟世界座標來做個轉換。
首先來看從UI座標轉世界座標,UGUI有自己的座標設定,它的Pivot point預設在中心(0.5, 0.5)的位置,所以在UI中心的Local的座標位置就是(0, 0),先來看一張簡單的示意圖。
從這張示意圖來看,假設遊戲解析度是(800*600),同時UGUI的canvas設定的也是(800*600),紅色的線條標示的就是Viewport的座標,左下角(0, 0),右上角(1, 1)。
在這個UI上我放置了一個Text物件,它在Canvas下的Local座標是(164.7, 100.2),但是因為原點在中心,所以如果把原點換到左下角,這個物件的座標就是(564.7, 400.2),在經過換算就可得到VIewport的位置是(0.706, 0.667)。
到這邊就可以經由Camera.ViewportToWorldPoint()這個方法來求得這個UI座標的位置在World的位置。
當然也可以直接用該Text的Position來得到這個物件的世界座標,不過如果有時候沒有物件單純只有一個UI座標的時候就會需要用到轉換。
從UI座標轉換到世界座標就這樣,相反的從世界座標轉換到UI座標也差不多。
先使用Camera.WorldToViewportPoint()把物件從世界座標轉換成Viewport座標,例如得到的物件Viewport座標是(0.706, 0.667)的位置,經過換算:
x = (0.706 * 800) - 400 = 164.8; y = (0.667 * 600) - 300 = 100.2;
也就可以得到它在UI中的Local位置應該是(164.8, 100.2)。
到目前為止都還算可以,這是因為遊戲畫面的解析度(800*600)跟UI的canvas設定的解析度一樣,但是當設定的數值不同時就會造成一點差異。
這個示意圖來看,遊戲畫面的解析度是(960*600),而canvas的設定是(800*600)並且使用matchWidthOrHeight的設定。
因為matchWidthOrHeight是以Width為準,所以寬的pixels實際上不改變,而高的pixels實際上會變為500,因為用(960*600)的比例去調整。
Width = 800; //不改變 Height = (600 / 960) * 800 = 500; //依據Width調整
因此上面那個UI物件的Local座標不變,但是就會跟原本,遊戲與UI相同解析度所得到的資料不同了。
而如果matchWidthOrHeight中,Match的數值不是0或1的時候又會更麻煩一點,這邊可以簡單參考UI Canvas物件RectTransform中的數值。
從這個UI的Rect Transform當中可以看到,雖然我CanvasScaler是設定(800*600),但是使用了matchWidthOrHeight,所以Rect Transform中的Width跟Height自動調整到了(800*500)。
因此可以使用RectTransform中的Rect的數值,取得當前Width跟Height的Pixels數值。
基本上也可以使用CanvasScaler中的Resolution、Width值、當前螢幕解析度,來去計算這兩個值,不過就簡單直接使用這個就好了。
參考Class,這邊我是把它做成Static直接呼叫使用。
public class UIPosition { static public Vector2 WorldToUI(RectTransform r, Vector3 pos) { Vector2 screenPos = Camera.main.WorldToViewportPoint(pos); //世界物件在螢幕上的座標,螢幕左下角為(0,0),右上角為(1,1) Vector2 viewPos = (screenPos - r.pivot) * 2; //世界物件在螢幕上轉換為UI的座標,UI的Pivot point預設是(0.5, 0.5),這邊把座標原點置中,並讓一個單位從0.5改為1 float width = r.rect.width / 2; //UI一半的寬,因為原點在中心 float height = r.rect.height / 2; //UI一半的高 return new Vector2(viewPos.x * width, viewPos.y * height); //回傳UI座標 } static public Vector3 UIToWorld(RectTransform r, Vector3 uiPos) { float width = r.rect.width / 2; //UI一半的寬 float height = r.rect.height / 2; //UI一半的高 Vector3 screenPos = new Vector3(((uiPos.x / width) + 1f) / 2, ((uiPos.y / height) + 1f) / 2, uiPos.z); //須小心Z座標的位置 return Camera.main.ViewportToWorldPoint(screenPos); } }
如果有任何問題或錯誤歡迎提出。
1 comment:
這篇文章很詳細,感謝分享!
Post a Comment