這里我們先談第一個問題坐標矩陣變化實現布局自適應。
選取基准尺寸
通常你需要選擇一個基准的屏幕尺寸,象現在開發的應用也需要跨平台在iOS(iPhone/iPad)/Android都可以運行,我這邊選取的是iphone4的屏幕尺寸: 480 * 320. 設計師設計的GUI的素材時就是按照這個尺寸來設計。但是緊接着的問題是如何保證它在其他不同尺寸/分辨率的平台上運行時不會出現各種詭異的位置大小錯亂了。
舉一個實際的例子來更好描述這個問題,比如我們的游戲是水平方向的, 然后游戲進行中間的暫停界面中,有三個角落有按鈕或着標簽,屏幕中間有一個按鈕,如下圖所示:
很簡單的代碼:
void OnGUI()
{
GUI.Box(new Rect(15 , 10, 83, 49), bg_score);
GUI.Box(new Rect(372, 10, 98, 44), bg_time);
if (GUI.Button(new Rect(5, 280, 67, 41), bt_pause))
{
//pause the scene
}
}
伸縮居中
在Unity中我們將Game窗口的模式選擇為iPhone Wide(480x320), 然后運行游戲, 木有什么問題。 緊接着嘗試運行在iPhone 4G Wide(960x640), 你就會發現問題了,整個格局錯亂了,並沒有有比例的伸縮,然后全部堆到了左邊.
所以可以想到既然有了基准的屏幕尺寸,其他尺寸的處理必然需要針對這個基准來變化, 我們需要的是在基准屏幕上各個元素都按照一定的比例放大或者縮小,水平和豎直方向的伸縮比列一定得是同步的,這樣一來保證它們之間的相對位置保持不變。在Unity中GUI系統中我們就需要運用到GUI.Matrix矩陣變化。
要解決這個問題,我們可以定義一個基准尺寸,我們這里是480*320.
public static Vector2 NativeResolution = new Vector2(480, 320);
然后了,我們要讓長寬按照這個基准來變化,包括首先是伸縮放大或縮小,其次是在變化之后使其保持居中。
private static float _guiScaleFactor = -1.0f;
private static Vector3 _offset = Vector3.zero;
static List<Matrix4x4> stack = new List<Matrix4x4> ();
public void BeginUIResizing()
{
Vector2 nativeSize = NativeResolution;
_didResizeUI = true;
stack.Add (GUI.matrix);
Matrix4x4 m = new Matrix4x4();
var w = (float)Screen.width;
var h = (float)Screen.height;
var aspect = w / h;
var offset = Vector3.zero;
if(aspect < (nativeSize.x/nativeSize.y))
{
//screen is taller
_guiScaleFactor = (Screen.width/nativeSize.x);
offset.y += (Screen.height-(nativeSize.y*guiScaleFactor))*0.5f;
}
else
{
// screen is wider
_guiScaleFactor = (Screen.height/nativeSize.y);
offset.x += (Screen.width-(nativeSize.x*guiScaleFactor))*0.5f;
}
m.SetTRS(offset,Quaternion.identity,Vector3.one*guiScaleFactor);
GUI.matrix *= m;
}
public void EndUIResizing()
{
GUI.matrix = stack[stack.Count - 1];
stack.RemoveAt (stack.Count - 1);
_didResizeUI = false;
}
緊着這我們在OnGUI方法中的開頭和結尾分別調用BeginUIResizing和EndUIResizing來變化矩陣。
void OnGUI()
{
BeginUIResizing(); //call this in the beginning of method
GUI.Box(new Rect(15 , 10, 83, 49), bg_score);
GUI.Box(new Rect(372, 10, 98, 44), bg_time);
if (GUI.Button(new Rect(5, 280, 67, 41), bt_pause))
{
//pause the scene
}
EndUIResizing(); //call this in the end of method
}
這里我們根據長寬比,算出伸縮比例,然后為了保證伸縮之后的畫面能始終在屏幕中間,我們要算出多出來偏移量,最后我們根據這個偏移量和縮放比例對矩陣進行變化。
然后我們再在分辨率為960*640的情況下運行。
- iPhone4 960 * 640:
- iPhone5 1136 * 640:
- iPad 1024 * 768:
你可以看到在其他尺寸的屏幕上伸縮都沒有問題,而且元素都居中。但是有一個問題,你發現在iPhone5和iPad上幾個標簽按鈕的位置有點不太對,他們需要像iPhone4一樣緊貼兩邊,而在iPhone5和iPad上卻不是這樣。
偏移量
要想解決這個問題的理解這個矩陣變化是如何工作的。這個偏移量是變換之后算出來的偏移量。所以要想讓GUI元素在變換之后依然在保持屏幕的邊緣,我們需要將x,y減去這偏移量,於是有了下面的代碼:
void OnGUI()
{
BeginUIResizing(); //call this in the beginning of method
GUI.Box(new Rect(15 - offset.x/guiScaleFactor , 10 - offset.y/guiScaleFactor,
83, 49), bg_score);
GUI.Box(new Rect(372 + offset.x/guiScaleFactor, 10 - offset.y/guiScaleFactor,
98, 44), bg_time);
if (GUI.Button(new Rect(5 - offset.x/guiScaleFactor, 280 + offset.y/guiScaleFactor,
67, 41), bt_pause))
{
//pause the scene
}
EndUIResizing(); //call this in the end of method
}
這里要記住這個偏移量是offset.x/guiScaleFactor, 而不是offset.x, 因為這個坐標是基於基准矩陣來的。
將代碼重新跑一遍:
- iPhone5 1136*640
- iPad 1024 * 768:
這個木有問題了。