1 尺寸與位置
通過UIWidget類獲取,該類是所有UI元素的基類
在unity中創建一個sprite,如下圖所示
這里面這個sprite的大小受幾個屬性的影響,首先是屬性面板里的Size,對應的UIWidget成員為width和height字段,然后是其上層節點和自身scale屬性。
1.1 局部大小
public override Vector3[] worldCorners { get { Vector2 offset = pivotOffset; float x0 = -offset.x * mWidth; float y0 = -offset.y * mHeight; float x1 = x0 + mWidth; float y1 = y0 + mHeight; Transform wt = cachedTransform; mCorners[0] = wt.TransformPoint(x0, y0, 0f); mCorners[1] = wt.TransformPoint(x0, y1, 0f); mCorners[2] = wt.TransformPoint(x1, y1, 0f); mCorners[3] = wt.TransformPoint(x1, y0, 0f); return mCorners; } }
從上面可以看出局部角點對應的矩形寬和長就等於width和height(mWidth等於width)
1.2 世界坐標系大小
所以計算某個UI元素在屏幕上的真實大小應該為
public override Vector3[] worldCorners { get { Vector2 offset = pivotOffset; float x0 = -offset.x * mWidth; float y0 = -offset.y * mHeight; float x1 = x0 + mWidth; float y1 = y0 + mHeight; Transform wt = cachedTransform; mCorners[0] = wt.TransformPoint(x0, y0, 0f); mCorners[1] = wt.TransformPoint(x0, y1, 0f); mCorners[2] = wt.TransformPoint(x1, y1, 0f); mCorners[3] = wt.TransformPoint(x1, y0, 0f); return mCorners; } }
從里面取兩個四個角點之間的距離即可算出真實的大小(因為Transform類獲取不到derived scale屬性,只能獲取到localScale屬性)
上面代碼中也用到了pivotOffset,該屬性對應於在編輯器選中UI元素局部坐標系的原點,默認的情況下該值是(0.5,0.5),表示在UIWidget中的中心,為(0,0)表示在左下角,(1,1)表示在右上角。
同時根據上面的worldCorners也很好獲取該UI元素在屏幕上的位置。這里面世界位置的計算中,可以看出同時用到了定義UI元素時的大小、坐標中心,以及該UI元素的轉置矩陣。
所以說,假如需要改變UI元素的位置,只需改變其轉置矩陣里的位置即可。
1.3 相對於其他元素的位置
采用通用的計算過程,即假如要計算B相對於A的位置,只需給出A和B的世界坐標PA和PB,那么B相對於A的位置即為 Rba = PB- PA。
NGUI里對應的代碼如下:
public override Vector3[] GetSides (Transform relativeTo) { Vector2 offset = pivotOffset; float x0 = -offset.x * mWidth; float y0 = -offset.y * mHeight; float x1 = x0 + mWidth; float y1 = y0 + mHeight; float cx = (x0 + x1) * 0.5f; float cy = (y0 + y1) * 0.5f; Transform trans = cachedTransform; mCorners[0] = trans.TransformPoint(x0, cy, 0f); mCorners[1] = trans.TransformPoint(cx, y1, 0f); mCorners[2] = trans.TransformPoint(x1, cy, 0f); mCorners[3] = trans.TransformPoint(cx, y0, 0f); if (relativeTo != null) { for (int i = 0; i < 4; ++i) mCorners[i] = relativeTo.InverseTransformPoint(mCorners[i]); } return mCorners; }
1.4 屏幕位置
上面討論的位置均是3D空間的位置,而非屏幕上的位置。
要獲取一個UI元素在屏幕上的位置,使用NGUI的攝像頭將世界坐標轉換到屏幕坐標
pt=UICamera.currentCamera.WorldToScreenPoint(pt);
對於置於世界原點的攝像機,它的光軸也穿過世界原點,假如使用
Vector3 screenPt = cam.WorldToScreenPoint(new Vector3(0,0,cam.nearClipPlane);
來計算光軸中心點在屏幕上的位置,可以發現算出來的屏幕位置也為屏幕中心,即(Screen.width/2,Screen.height/2,cam.nearClipPlane);
在NGUI中,2DUI的攝像機是正交攝像機,3D的才是透視攝像機。
同時正交攝像機中
屏幕的右上角對應的世界坐標為:
Vector3 worldPt = cam.ScreenToWorldPoint(newVector3(Screen.width,Screen.height,uiCam.nearClipPlane));
//worldPt = (cam.size*(Screen.width/Screen.height),cam.size,cam.nearClipPlane);
左下角的世界坐標為:
Vector3 worldPt1 = cam.ScreenToWorldPoint(newVector3(0,0,uiCam.nearClipPlane));
//worldPt = (-cam.size*(Screen.width/Screen.height),-cam.size,cam.nearClipPlane);
這里面假設的是相機填滿整個屏幕,即相機的Rect屬性的w和h均為1
2 實例
2.1 將UI移動屏幕指定像素位置
usingUnityEngine; usingSystem.Collections; publicclassSetPos:MonoBehaviour { publicint targetX; publicint targetY; public Camera uiCam; private UIWidget ui; voidStart() { ui=GetComponent<UIWidget>(); SetScreenPosition(targetX, targetY); } voidSetScreenPosition(int x,int y) { Vector3 targetPos = uiCam.ScreenToWorldPoint(newVector3(x, y, transform.position.z)); Vector3 dir = targetPos - transform.position; transform.Translate(dir); } voidSetScreenPosition1(int x,int y) { //使用線性映射計算目標位置 float w =Screen.width; float h =Screen.height; float posX = uiCam.orthographicSize* w / h; float posY = uiCam.orthographicSize; float targetX = x *2* posX / w - posX; float targetY = y *2* posY / h - posY; Vector3 targetPos1 =newVector3(targetX, targetY,0); Vector3 dir = targetPos1 - transform.position; transform.Translate(dir); } }