說起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相機,都可以掛載這個腳本做適配。這樣算是一個完美的解決方案了吧,至於黑邊,有時間再優化。