2011年4月30日 星期六

Unity 自動調整 GUI 縮放比例及位置

面對各平台的畫面解析度大小不同,常常會有困擾於 UI 位置及大小要做好幾套來應對,或是利用程式去多方演算以達到各種不同需求都能適中,以免辛苦調整好的 UI 在面臨不同解析度下,整個位置及大小都偏移掉了。

一般而言,我們通常會利用 Screen.width 及 Screen.height 取得當前的畫面寬高,然後 UI 位置從四面邊緣往內算其相對位置,這樣的話可以避免掉因解析度大小不同而有位置座標超出畫面外的問題,但是光是如此做只能避免位置錯誤,但 UI大小卻不變,而有可能因解析度不同,在畫面縮放下產生UI 重疊的問題,所以呢,我們可能需要去計算整體的縮放比例來改變每個 GUI 大小及位置。這個比例的計算方式通常我們會先定義一個基數,例如輸出到 iPhone 的解析度設定為 640*960 ,那麼我們可以求出一個縮放比例倍數,大致如下:

//畫面基數
private var baseW : float = 640;
private var baseH : float = 960;

//縮放倍數
private var multipleW : float = Screen.width / baseW;
private var multipleH : float = Screen.height / baseH;

之後我們就可以在每個 GUI 的 Rect( x , y , w , h) 乘上這個倍數,那麼位置及大小在任何解析度下都會是適合的,而我們在撰寫程式時,GUI 的位置座標也就都統一以左上角為原點,不用再去依照 Screen.width 及 Screen.height 去計算這個 GUI 位置應該是 Screen.width 減多少或 Screen.height 減多少囉~

這個方法雖然好,但是每行有關 GUI 的程式將會變得更長,而且還要不斷對每個 GUI 的 x、y 座標及寬高乘上這個倍數,程式可讀性降低了,若有哪個乘錯了或是漏掉,在維護上也造成一定程度的麻煩,還有開發速度上,為了不斷的貼上重複的程式碼,效率上也變得較差。為此,我們必須找個更方便、更簡單、更有效率的方法。

此時 GUI.matrix 及 GUIUtility.ScaleAroundPivot() 就可以派上用場了,這兩個都可以讓我們滿足我們相同的需求,只有用法上有一點點小小的不同而已,利用他們加在 OnGUI() 的開頭處,之後的 GUI 只需要依照平常的一般用法,不用刻意做任何改變或乘上縮放倍數,即可依照任何不同畫面解析度自動將每個 GUI 調整為適當大小及位置,以下提供兩個用法的範例:

/**** matrix ****/
//畫面基數
private var baseW : float = 640.0;
private var baseH : float = 960.0;
//比例
private var scale : Vector3;

function Awake(){
      scale = Vector3(parseFloat(Screen.width)/baseW,parseFloat(Screen.height)/baseH,1);
}

function OnGUI(){
  
    GUI.matrix = Matrix4x4.Scale(scale);
  
    /* .... 其他GUI ....*/
}


/**** GUIUtility ****/
//畫面基數
private var baseW : float = 640;
private var baseH : float = 960;
//比例
private var scale : Vector2;

function Awake(){
    scale = Vector2(parseFloat(Screen.width)/baseW,parseFloat(Screen.height)/baseH);
}

function OnGUI(){
  
    GUIUtility.ScaleAroundPivot(scale,Vector2.zero);
  
    /* .... 其他GUI ....*/
}


以上,是不是很簡單呢!

2011/5/14 修正...
本文最初發文時有提到我在輸出到 Android 手機時,寬度及 X 軸位置都有自動調整了,但高度及 Y 軸位置卻無法如預期調整,此部份問題已於上面的範例內容修正,主要原因為 Screen.width 與 Screen.height 的資料型態為 int ,與 float 一起運算時會有一些因型態轉換上造成的不正確, Unity 的 JavaScript 在型態自動轉換上似乎沒有一般JavaScript 好,所以需要使用到 parseFloat(),另外,在 PC 或 iPhone 上,宣告變數時可直接給運算式,但在 Android 上似乎行不通,所以宣告後改至 Awake() 裡面再計算,以上兩點修正後,在 OnGUI 中執行的 GUI 就能夠自動依照任何畫面解析度完全自動調整囉~
另外,如果是使用 GUI Text 或 GUI Texture ,本文介紹的方法是沒有用的,因為 GUI Text 與 GUI Texture是以 GameObject 的方式配置在場景中,並非在 OnGUI 中運行的。