文章出處
文章列表
一、當下移動設備的主流分辨率(數據來自“騰訊分析移動設備屏幕分辨率分析報告”)
1.1 iOS設備的分辨率主要有:
寬 | 高 | 寬高比 |
960 | 640 | 1.5 |
1136 | 640 | 1.775 |
1024 | 768 | 1.3333 |
2048 | 1536 | 1.3333 |
Android設備的分辨率則相對紛雜,主流的分辨率有:
寬 | 高 | 寬高比 |
800 | 480 | 1.6667 |
854 | 480 | 1.7792 |
1280 | 720 | 1.7778 |
960 | 540 | 1.7778 |
1280 | 800 | 1.6 |
960 | 640 | 1.5 |
1184 | 720 | 1.6444 |
1920 | 1080 | 1.7778 |
二、NGUI默認的多分辨率適配原則
NGUI本身按照“高度適配”的原則進行多分辨率下的UI適配,其默認的高度通過 UIRoot.manualHeight 設置。再配合使用 UIAnchor 便可實現一定程度的多分辨率適配。
其中,在Unity Editor下按照 UIRoot.manualHeight 設定的高度,編輯UI頁面。這樣,當UI頁面在目標設備上顯示時,NGUI按照目標設備的高度(targetHeight)來調整UIRoot節點的scale,以使整個UI頁面適應目標設備的高度。比如manualHeight=400,而targetHeight=800,那么UIRoot的scale將被乘以2。所以,當目標設備的寬高比與所編輯頁面的寬高比一致時,整個UI將完美顯示;當目標設備寬高比小于所編輯的寬高比時,頁面寬度將大于設備寬度,使得多出的部分無法顯示;而當目標設備寬高比大于所編輯寬高比時,頁面寬度小于設備寬度,設備兩邊將出現黑邊。
而UIAnchor則是將整個頁面分為TopLeft/Top/TopRight/Left/Center/Right/BottomLeft/Bottom/BottomRight九個區域,掛載了UIAnchor組件的節點都將按照設置自動停靠到相應的區域中。有了UIAnchor,上面的兩個問題將被一定程度的解決:當目標設備寬高比小于編輯的寬高比時,由于UIAnchor的自動停靠功能,UI不會被裁切掉,但UI之間的左右間距將相應變小,便有可能出現UI重疊的問題;當目標設備寬高比大于所編輯寬高比時,UI之間的左右間距將變大,好在這樣起碼不會有UI被裁切或重疊。
看似我們只需要解決UI重疊的問題就搞定了。不過讓我們再仔細想一下,一張鋪滿整個屏幕的UISprite不管是否使用UIAnchor,在目標設備寬高比更小時,sprite都會在橫向上被裁切,而將目標設備寬高比更大時,sprite都不能鋪滿整個屏幕。
問題出來了:
1. 當目標設備寬高比更小時的UI重疊問題
2. 當目標設備寬高比更小時,全屏sprite被裁切問題
3. 當目標設備寬高比更大時,全屏sprite不能鋪滿整個屏幕的問題
三、解決問題
首先定義幾個變量:
standard_width 編輯頁面的原始寬度
standard_height 編輯頁面的原始高度
device_width 目標設備的寬度
device_height 目標設備的高度
standard_aspect 編輯頁面的寬高比
device_aspect 目標設備的寬高比
1. 目標設備寬高比更小時的UI重疊問題
當device_aspect小于standard_aspect時,UIRoot根據device_height調整其scale大小,因而使得設備寬度不足以顯示整個頁面。我們調整Camera.orthographicSize(僅適用2D GUI),以足夠顯示頁面的寬度。令
Camera.orthographicSize = standard_aspect / device_aspect;
即,改變了NGUI原有的“高度適配”原則,轉為“寬度適配”,使得整個頁面都得以顯示,而由于UIAnchor的存在,UI的左右間距保持不變,但上下間距會變大。
該方法可以實現為一個MonoBehaviour腳本(UICameraAdjustor.cs),掛載到UICamera同一個節點上,代碼如下:
1 using UnityEngine; 2 using System.Collections; 3 4 /// <summary> 5 /// 根據設備的寬高比,調整camera.orthographicSize. 以保證UI在不同分辨率(寬高比)下的自適應 6 /// 須與UIAnchor配合使用 7 /// 將該腳本添加到UICamera同一節點上 8 /// </summary> 9 10 [RequireComponent(typeof(UICamera))] 11 public class UICameraAdjustor : MonoBehaviour 12 { 13 float standard_width = 1024f; 14 float standard_height = 600f; 15 float device_width = 0f; 16 float device_height = 0f; 17 18 void Awake() 19 { 20 device_width = Screen.width; 21 device_height = Screen.height; 22 23 SetCameraSize(); 24 } 25 26 private void SetCameraSize() 27 { 28 float adjustor = 0f; 29 float standard_aspect = standard_width / standard_height; 30 float device_aspect = device_width / device_height; 31 32 if (device_aspect < standard_aspect) 33 { 34 adjustor = standard_aspect / device_aspect; 35 camera.orthographicSize = adjustor; 36 } 37 } 38 }
總之,在使用該方法后,當device_aspect大于standard_aspect時,UI按照高度適配原則,UI的上下間距不變,左右間距變大;當device_aspect小于standard_aspect時,UI按照寬度適配原則,UI的左右間距不變,上下間距變大。
2. 目標設備寬高比更小時,全屏sprite被裁切問題
全屏背景的sprite被裁切可能在很多情況下不會成為什么問題,但在我們使用了解決問題1中的方法后,這里的“被裁切問題”就變為了同問題3類似的“不能鋪滿整個屏幕問題”。解決方法是放大sprite scale:
sprite.transform.localScale *= ( standard_aspect / device_aspect );
這樣會使得sprite在橫向上被裁切,寬高比不同必然的結果... 當然也可以選擇只調整高度或寬度,只要能接受變形...
3. 目標設備寬高比更大時,全屏sprite不能鋪滿整個屏幕的問題
同問題2,解決方法同樣是放大sprite scale:
sprite.transform.localScale *= ( device_aspect / standard_aspect );
這樣會使得sprite在縱向上被裁切。
問題2和3的解決方法相應腳本(UIBackgroundAjustor.cs)會在文章后面給出。
該腳本須掛載到sprite同一節點上,配合UIAnchor使用,可以選擇是裁切方向。如UIAnchor停靠方式使用center,則sprite會被左右兩邊或上下裁切,若使用Top,則會左右裁切或下邊裁切。
總之,全屏sprite會始終鋪滿整個屏幕,不會出現黑邊。當device_aspect大于standard_aspect時,全屏sprite按照寬度適配,縱向裁切;當device_aspect小于standard_aspect時,按照高度適配,橫向裁切。
四、優化
1. UI頁面的制作尺寸按 1024 X 600
前面講到主流分辨率的情況,其平均寬高比(除ipad2/3/4以外)大概為1.7,與主流的寬高比都不會偏差很大。即,在使用上面的多分辨率解決方法時,UI不會在縱向或橫向上的間距過大,顯得特別離譜。按照此寬高比,我們選擇1024x600的尺寸來制作UI,并嚴格要求UI制作時,頁面分為TopLeft/Top/TopRight/Left/Center/Right/BottomLeft/Bottom/BottomRight九個區域,以便掛載UIAnchor。
2. 全屏背景的制作按 1024 X 768
如果全屏背景圖也按1024 x 600制作,在ipad2/3/4上就會有較大程度的放大。同時考慮到NGUI的打包atlas,使用2的冪次尺寸,高度600和768都將占用1024x1024的atlas。所以全屏背景在制作時,高度上做出一定的冗余尺寸,以使寬高比小于1.7時,高度上放大系數不會太大,避免圖片嚴重失真。
加入冗余尺寸后的腳本(UIBackgroundAjustor.cs)如下:
1 using UnityEngine; 2 using System.Collections; 3 4 /// <summary> 5 /// 根據設備的寬高比,調整UISprite scale, 以保證全屏的背景圖在不同分辨率(寬高比)下的自適應 6 /// 將該腳本添加到UISprite同一節點上 7 /// 須與UICameraAdjustor腳本配合使用 8 /// </summary> 9 10 [RequireComponent(typeof(UISprite))] 11 public class UIBackgroundAdjustor : MonoBehaviour 12 { 13 float standard_width = 1024f; 14 float standard_height = 600f; 15 float device_width = 0f; 16 float device_height = 0f; 17 18 void Awake() 19 { 20 device_width = Screen.width; 21 device_height = Screen.height; 22 23 SetBackgroundSize(); 24 } 25 26 private void SetBackgroundSize() 27 { 28 UISprite m_back_sprite = GetComponent<UISprite>(); 29 30 if (m_back_sprite != null && UISprite.Type.Simple == m_back_sprite.type) 31 { 32 m_back_sprite.MakePixelPerfect(); 33 float back_width = m_back_sprite.transform.localScale.x; 34 float back_height = m_back_sprite.transform.localScale.y; 35 36 float standard_aspect = standard_width / standard_height; 37 float device_aspect = device_width / device_height; 38 float extend_aspect = 0f; 39 float scale = 0f; 40 41 if (device_aspect > standard_aspect) //按寬度適配 42 { 43 scale = device_aspect / standard_aspect; 44 45 extend_aspect = back_width / standard_width; 46 } 47 else //按高度適配 48 { 49 scale = standard_aspect / device_aspect; 50 51 extend_aspect = back_height / standard_height; 52 } 53 54 if (extend_aspect >= scale) //冗余尺寸足以適配,無須放大 55 { 56 } 57 else //冗余尺寸不足以適配,在此基礎上放大 58 { 59 scale /= extend_aspect; 60 m_back_sprite.transform.localScale *= scale; 61 } 62 } 63 } 64 }
文章列表
全站熱搜