天天看點

nguI 螢幕自适應

一、當下移動裝置的主流分辨率

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;

4 /// <summary>

5 /// 根據裝置的寬高比,調整camera.orthographicSize.   以保證UI在不同分辨率(寬高比)下的自适應

6 /// 須與UIAnchor配合使用

7 /// 将該腳本添加到UICamera同一節點上

8 /// </summary>

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;

4 /// <summary>

5 /// 根據裝置的寬高比,調整UISprite scale, 以保證全屏的背景圖在不同分辨率(寬高比)下的自适應

6 /// 将該腳本添加到UISprite同一節點上

7 /// 須與UICameraAdjustor腳本配合使用

8 /// </summary>

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 }