2015年4月8日 星期三

Unity:UGUI 應對各種螢幕自動調整大小及位置

以前曾經發佈過兩篇有關 GUI 自動調整的文章「Unity 自動調整 GUI 縮放比例及位置」以及「Unity:應對各種螢幕比例自動調整畫面縮放及位置」,
自從 Unity 於 4.6 版發佈了新的 GUI 系統(UGUI)之後,使用 Unity 製作 UI 變得更為簡單方便,同時也比較不需要依賴第三方製作的 UI 工具了,在事件系統上也有了一些革新,UI 事件與 Component 之間變得更加視覺化,彼此間的耦合度大大降低,而使得使用上更為彈性,雖然,UGUI 解決了許多以前 UI 製作上面的問題,但實際開發時,還是有些部分需要使用者做些調整。

在 Unity 4.6 發佈之後,UGUI 相關的遊戲物件,不像一般的遊戲物件使用 Transform,而是 Rect Transform,其中多了寬、高以及 Anchors、Pivot 等欄位,這些欄位對 UI 製作的視覺化設計上有很大的幫助,而其中的 Anchors 更是讓 UI 整體應對於各種不同比例畫面有著更加強大及彈性的控制權。

如果在 Unity 的 Game view 使用 Free Asspect 觀察畫面時,任意的拉動視窗邊緣去調整畫面比例,可以發現 UGUI 本身是並不會移動位置或縮放大小,如果將 320x480 變為 480x320,就有可能使 UI 被畫面邊緣切掉,這時候 Anchors 就能發揮很大的用處,使得 UI 圖片或按鈕等元素都能跟隨畫面比例的改變而變更其寬高大小,但其美中不足的是文字並不會因此跟隨改變大小,當然,如果文字有勾選 Best Fit 的話,也會有自動變更 Size 的效果,只是,按鈕、圖片等 UI 元素跟隨畫面比例改變,也改變了本身的寬高比例,但文字卻只是改變 Font Size,所以,當畫面比例改變了,圖片變形了,文字並未跟著變形,那麼文字與其他 UI 之間的距離或是按鈕邊緣與按鈕內的文字的間隙就可能會發生奇怪的變化,這些都是我們所不希望看到的,所以,最好的狀況應該是,當畫面比例改變了,UI 的圖片、文字、按鈕等文字都能維持原本的長寬比例自動縮放大小及位置。

正常的縱向畫面
轉為橫向時,畫面被切掉了
為 UI 設置好 Anchors
轉為橫向後,UI 自動變形調整,但文字不同



當在場景中建立第一個 UGUI 的遊戲物件時,一定會先自動建立 Canvas 的遊戲物件,在這個物件中,預設配置了 Canvas、Canvas Scaler、Graphic Raycaster 這三個 Component,其中的 Canvas Scaler 正是用來控制 UGUI 整體的縮放,所以只要依照以下步驟,就能達到最基本的依照原比例調整 UI 的目的:
  1. 讓所有的 UI 的 Anchors 維持在預設值(0.5)。
  2. 將 Canvas Scaler 的 Ui Scale Mode 欄位設置為 Scale With Screen Size。
  3. 在 Reference Resolution 欄位輸入基礎解析度的寬(X)、高(Y)。
  4. Screen Match Mode 欄位選擇 Match Width Or Height。
  5. 如果畫面為橫向,Match 欄位選輸入 0,如果畫面為縱向,Match 欄位輸入 1。
讓 Anchors 維持在預設值 0.5
設置 Canvas Scaler 的相關欄位
如此,當畫面從縱向轉為橫向,GUI 就會自動維持原比例調整其大小及位置。

轉為橫向畫面,UI 的比例及位置不變
但是,這是在比例相同直接橫向轉縱向時才會如此理想,當遇到比例不同的畫面,例如原本是 iPhone 4 的 640x960 設計的 UI 佈局,遇上了 iPhone 5 的 640x1136 畫面,就會發生左右兩邊被切掉的情形。

當比例不同時,邊緣被切掉了
這時我們就必須將 Canvas 遊戲物件的 Canvas Scaler componet 的 Match 欄位改為 0,使其 UI 依照原本比例縮放調整到畫面內。

調整 Canvas Scaler 的 Match 使被切掉的部分回到畫面內。
為了因應可能遇到各種未知的畫面比例(特別是 Android 以及桌機平台),我們可以為 Canvas 掛上包含以下這一段程式碼的 component 來自動調整 Canvas Scaler component 的 Match 欄位。

void Awake(){

    CanvasScaler canvasScaler = GetComponent<CanvasScaler>();

    float screenWidthScale = Screen.width / canvasScaler.referenceResolution.x;
    float screenHeightScale = Screen.height / canvasScaler.referenceResolution.y;

    canvasScaler.matchWidthOrHeight = screenWidthScale > screenHeightScale ? 1 : 0;
}

完成以上的工作,將來在 UI 製作上就會輕鬆很多,如果沒有特殊需求,基本上不需要動到 UI 的 Anchors 值,大部份都只是單純的調整 UI 大小及位置就行了,面對各種畫面比例,也將維持原設計的佈局自動調整,另外,如果需要製作 UI 的動態縮放、位移等等使畫面更生動的話,以變更其 Local space 的數值為主,那麼比例縮放的部分都不需要特別去考慮,Canvas Scaler 會幫我們處理。

不過,有一個例外,目前是無法使用此辦法解決的,就是當 UI 文字使用了 Rich Text 的 Size 標籤去指定文字大小時,文字的大小是不會受到 Canvas Scaler 影響的,它指定了絕對的文字 Size,這一點要特別注意。

附帶一提,除了 UI 佈局以及變化之外,有時候也會想在 UI 上做些粒子特效,或是讓非 GUI 的遊戲物件在畫面上與 UI 呈現互動的效果,此時,只要把 Canvas 遊戲物件的 Canvas component 的 Render Mode 欄位改為 Screen Space - Camera 並設定好 Render Camera 及 Plane Distance 的內容,那麼放置於 UI 平面與其指定的 Camera 中間的物件就可以顯示於 UI 之上與其互動,如果是 Sprite 遊戲物件,則可以依據 Sorting Layer 與 GUI 之間調整前後順序;不過,當遇到自動調整過的 GUI 畫面,這些非 GUI 的遊戲物件大小及位置可能會與預期的不同,為了避免此情況發生,可以參考之前發佈過的文章內容「Unity:應對各種螢幕比例自動調整畫面縮放及位置」去調整 Camera。

改變 Canvas 的 Render Mode 為 Screen Space - Camera
P.S. 目前使用 Unity 版本為 5.0.1f1。