Unity+NGUI多分辨率適配方案


  說起unity的適配方案,網上可謂是一查一大堆,但是真正要應用到項目中的時候,總會出現各式各樣的問題。由於最近自己要做一個小游戲,在開始做游戲之前,就想着先好好搞一搞適配這塊,以后新起項目的時候也會用得着。

NGUI應該是現在大部分開發者都會去選擇的UI插件,雖然NGUI還存在着不少問題,像是相對來說,NGUI還是比較靠譜的,所以這里只是針對NGUI做適配方案。

  NGUI中對於每一個場景,都是以UIRoot為GameObject樹的根的,UIRoot下面主要有這幾種屬性

  

1) Scaling Style

UIRoot有幾種縮放方式

  方式1:    PixelPerfect 這種方式下,你的UI一直都是以像素為基礎,一個300*200的widget在屏幕上永遠占用300*200像素。這就意味着,你的UI在低分辨率的機器上會顯得非常大,在高分辨率的機器上就會顯得很小。這個設置就是一直保持你的UI清晰。

  方式2:  FixedSize是一個和它功能正好相反的設置。當UIRoot用了這個選項,你的屏幕永遠都會保持NGUI所關心的尺寸,不管你的實際屏幕是多大。就是說一個300*200的widget占用了你1920*1080的25%的屏幕,那么當分辨率降低到1280*720的時候,它同樣占用25%的屏幕。如果你不介意你的UI看起來像是不同的尺寸,也不關心是否清晰(就是可能一個小的UI被拉伸很大),那么選擇這個選項。選擇它的時候,不要忘記設置Manual Height。 

這個占的百分比就是通過widget的尺寸和當前你設置的Manual的尺寸而算出來,所以當你移植到不同的移動設備時,它會根據這個占比和移動設備的實際分辨率進行運算,然后算出顯示的實際像素大小。即Scaling Style指定為FixedSize或FixedSizeOnMobiles,則縮放只以Manual Height為參考,屏幕分辨率的高度值不同於此設置值時,則根據其比例(即Screen Height / Manual Height)對整棵UI樹的進行“等比”縮放(寬度的縮放比也是此比例值)。

  方式3: FixedSizeOnMobile是前兩種的組合。選擇這個選項后,會在pc或者mac等桌面設備上用“PixelPerfect”,在移動設備上用“FixedSize”。 

如果你沒有選擇Fixed Size選項,那就要設置Minimum和Maximum Height的值。這些值讓你的虛擬屏幕看起來在合理范圍。比如選擇了Pixel Perfect方式,Minimum Height設置為720,那么當有玩家把你的程序運行在800*600(高度是600,小於Minimun Height)的設備上時,你UI的行為就和設置了“Fixed Size”模式、Manual Height值設為720的時候一樣。

這個Minimum和Maximum Height用於你對實際的屏幕尺寸進行限制,如果實際的屏幕尺寸小於Minimum,那么就相當於設置了“Fixed Size”模式、Manual Height值設為Minimum的時候一樣,同理,如果屏幕尺寸超過了Maximum,那也相當於設置了“Fixed Size”模式、Manual Height值設為Maximum的時候一樣。

下面用實際例子來驗證各個方式的實際效果

 

Pixel Perfect下,先將Minimum和Maximum Height 分別設置為500,1500,然后用NGUI做出期望UI,如下

 

下面是在各個分辨率下的實際顯示情況

1024*768

 

1920 * 1080

其他的就不一一羅列了,我們發現中間的圖片一直保持着原大小沒有變化,至於背景圖會超出屏幕外的問題,我們可以通過設置Anchor的方法來解決,將背景圖的四邊對齊UIRoot的四邊,四個角的方塊按鈕的Anchor以背景圖為目標,效果如下:

很明顯這樣的效果和期望設計UI的對比是讓人難以接受的,所以現在市面的手機游戲幾乎沒有使用Pixel Perfect做適配方案的.....

下面把Pixel Perfect改成Fix Size,UI背景圖和四個角的anchor保持不變,

這樣的效果是可以接受的,這里面還有一個問題,那就是因為設置了anchor,導致背景圖會有拉伸的情況,而且如果使用anchor來做適配的話,前端程序員的工作量太大了。所以我們還是去掉anchor,想想其他辦法。

這里面我們先將UIRoot設置為

然后1280 * 720 作為默認分辨率,然后背景圖大小也統一設置為1280 * 720,做好一個簡單的界面

然后將UI放到960 * 640的分辨率上顯示 ,因為1280 * 720 是16:9的屏幕,而960 * 640 是3:2的屏幕,所以會導致寬度上無法全部顯示出來

 

這時候有以下幾種解決方法

方法1:動態更改UIRoot的Manual Height

     在960 * 640的屏幕下,由於Manual Height = 720,所以720放到640的高度下需要縮放640/720 = 8/9, 由於UI是按照1280*720的尺寸設計的,所以UI的寬度縮為1280 * 8/9 = 1137.7,明顯960的寬度根本放不下,所以我們要繼續更改縮放系數,使其

能夠在960的寬度下完全顯示,而這個縮放系數是根據屏幕尺寸和Manual Height算出來的,所以我們只需要動態的去計算Manual Height,就可以很好的將UI整體放進屏幕中了。

  以前看過的代碼,掛到UIRoot下面

 1 using UnityEngine;  2 using System.Collections;  3 
 4 public class UIAdapter : MonoBehaviour {  5 
 6     public int ManualWidth = 1280;  7     public int ManualHeight = 720;  8 
 9     void Awake () 10  { 11  AdaptiveUI(); 12  } 13 
14     private void AdaptiveUI() 15  { 16 
17         UIRoot uiRoot = GetComponent<UIRoot>(); 18         if (uiRoot != null) 19  { 20             if (System.Convert.ToSingle(Screen.height) / Screen.width > System.Convert.ToSingle(ManualHeight) / ManualWidth) 21                 uiRoot.manualHeight = Mathf.RoundToInt(System.Convert.ToSingle(ManualWidth) / Screen.width * Screen.height); 22             else
23                 uiRoot.manualHeight = ManualHeight; 24  } 25  } 26 }

此時運行

 這種方法上下會留有黑邊,黑邊如果感覺不能忍受,也可以做一些處理,后面的方法也都是帶黑邊的,至於怎么優化這些黑邊,后面再研究。

 但是如果NGUI版本過低的話,這種方案可能會導致UIPanel的裁剪功能失效。所以再看看下面的方法。

 

方法2:調整UICamera的orthographicSize

這種方法即通過更改相機的正交尺寸來做UI自適應, 當相機為正交時,即像這樣:

 

看起來像是一個立方體的框框,這個框框就是相機所能顯示的范圍,orthographicSize變化時,框框的寬高會相應變化,比如我們將orthographicSize縮小為0.5

 看到相機的正交面也縮小了0.5,所以屏幕上顯示的UI也為原來的一半,那么上面提到的如果1280*720的圖片放到960*640的屏幕下,寬度不夠的問題,我們可以適當放大一下orthographicSize,這樣就可以將UI完全顯示到屏幕中。

直接上代碼,http://blog.csdn.net/dingkun520wy/article/details/26084045,感謝這位同學,只是代碼有點小錯誤,改過以后,掛到UIRoot的相機上

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(UICamera))]
public class UIAdapterCamera : MonoBehaviour
{
    float standard_width = 1280f;        //初始寬度  
    float standard_height = 720f;       //初始高度  
    float device_width = 0f;                //當前設備寬度  
    float device_height = 0f;               //當前設備高度  
    public float adjustor = 0f;         //屏幕矯正比例  
    void Awake()
    {

        //獲取設備寬高  
        device_width = Screen.width;
        device_height = Screen.height;
        //計算寬高比例  
        float standard_aspect = standard_width / standard_height;
        float device_aspect = device_width / device_height;
        //計算矯正比例  
        if (device_aspect < standard_aspect)
        {
            adjustor = standard_aspect / device_aspect;
            //Debug.Log(standard_aspect);  
        }
        Debug.Log("屏幕的比例" + adjustor);
        if (adjustor < 2 && adjustor > 0)
        {
            camera.orthographicSize = adjustor;
        }

    }
    // Use this for initialization  
    void Start()
    {

    }

    // Update is called once per frame  
    void Update()
    {

    }
}

然后960 * 640顯示是這樣子的

看到紅框處的數值由1變為1.18...了。OK。

到這里看起來這個方案應該可以了吧,其實這里面還有一個問題,那就是相機不是正交時,Orthographic size被忽略,就是說這個腳本只能用來解決2d相機的適配問題,某些非正交相機的適配還是無法實現,所以該方案也pass掉。

方法3: 更改相機的rect值,就是編輯器里面對應的ViewPort Rect

camera的rect默認為(0,0,1,1) ,(0,0)對應屏幕的左下角位置,(1,1)分別對應屏幕寬的百分比和高的百分比,所以默認相機的rect范圍是從左下角到屏幕右上角,即為全屏。

這樣的話,我們可以通過實際屏幕的分辨率和設計分辨率,來實際計算出相機視角的范圍

 上代碼,掛到camera下

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(Camera))]
public class MyCameraAdapter : MonoBehaviour
{

    public Camera camera;
 
    void Start()
    {
        AdaptCamera();
    }

    public void AdaptCamera()
    {
        if (camera == null)
        {
            camera = GetComponent<Camera>();
        }

        float screenAspect = Screen.width / Screen.height;
        float designAspect = 1280 / (float)720;

        if (designAspect < screenAspect) //屏幕分辨率過大,寬度過長,則屏幕橫向留出黑邊,高度不變
        {
            float tarWidth = Screen.height * designAspect;//求出實際要顯示的寬度
            float tarWidthRadio = tarWidth / Screen.width;//求出寬度百分比
            float posW = (1 - tarWidthRadio) / 2;//寬的起點
            camera.rect = new Rect(posW, 0, tarWidthRadio, 1);
        }
        else if (designAspect > screenAspect)//屏幕分辨率過小,高度過高,縱向留黑邊,寬度不變
        {
            float tarHeight = Screen.width  / designAspect;
            float tarHeightRadio = tarHeight / Screen.height;
            float posH = (1 - tarHeightRadio) / 2;
            camera.rect = new Rect(0, posH, 1, tarHeightRadio);
        }
        else
        {
            camera.rect = new Rect(0, 0, 1, 1);
        }
    }
}

 這樣的話無論是2d相機還是3d相機,都可以掛載這個腳本做適配。這樣算是一個完美的解決方案了吧,至於黑邊,有時間再優化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM