前段時(shí)間整理的一篇關(guān)于unity ui開發(fā)的文章,被推薦上了csdn首頁,對于剛剛寫文字的我來說,是莫大的鼓勵(lì),讓我干勁十足,寫出更多有質(zhì)量的文字。
寫在前面
屏幕適配是每個(gè)手機(jī)應(yīng)用和游戲都會(huì)解決的問題,當(dāng)然在開發(fā)的過程中會(huì)遇到各種各樣的坑,這次,我們就來討論一下unity項(xiàng)目中的屏幕適配吧!
目錄
- 屏幕適配的分類
- 哪些內(nèi)容需要適配
- unity中常見的適配方式
- 游戲內(nèi)容適配
- NGUI的適配方案
- UGUI的適配方案
屏幕適配的分類
說到屏幕適配的分類啊,也許會(huì)有所疑問,屏幕適配還能分類?細(xì)致分析一下,可以分為兩大類:分辨率適配和寬高比適配。
分辨率適配
首先得知道分辨率是什么?分辨率是屏幕顯示圖像的緊密度,是指顯示器能顯示的像素有多少。屏幕上的點(diǎn)、線、面都是由像素組成的,分辨率越高,同樣大小的屏幕能顯示的像素越多,畫面就越精細(xì)?,F(xiàn)在PC上分辨率大多是 1920 * 1080,我們看的視頻很多高清版本就是 1080p 的。
既然分辨率是屏幕的一項(xiàng)指標(biāo),那么手機(jī)上當(dāng)然也會(huì)用到,現(xiàn)在智能手機(jī)市場有那么多產(chǎn)品,有多個(gè)廠家生產(chǎn),并且有多個(gè)價(jià)位,所以手機(jī)屏幕分辨率肯定各不相同(雖然屏幕分辨率一般比較固定的幾個(gè))。那么分辨率適配是每個(gè)應(yīng)用、游戲都應(yīng)該做的。寬高比適配
這個(gè)很好理解,每個(gè)手機(jī)大小各不相同,寬高比也會(huì)有多種啦,適配寬高比當(dāng)然也是必須要做的嘍。
當(dāng)下移動(dòng)設(shè)備主流分辨率及寬高比:
iOS設(shè)備的分辨率主要有:
Android設(shè)備的分辨率則相對紛雜,主流的分辨率有:
哪些內(nèi)容需要適配
- User Interface
游戲UI需要適配,這是無所質(zhì)疑的,包括一般的手機(jī)應(yīng)用程序,都需要這個(gè)步驟。如果再細(xì)分一下,還分為位置適配和大小適配
位置適配:分辨率會(huì)影響UI在屏幕中顯示的位置,比如在800 * 600分辨率的屏幕上,button1在正中央位置,坐標(biāo)為(400, 300),但是如果放在1366 * 768分辨率屏幕上位置就會(huì)靠左邊一些,這樣會(huì)嚴(yán)重影響UI布局的美觀。
大小適配:同樣的例子,在800 * 600分辨率的屏幕上,button1的大小為50*20像素,但是到了分辨率高的屏幕上,button1就變得很小了,影響美觀,影響用戶正常使用。
- 游戲內(nèi)容
一般的應(yīng)用開發(fā),用戶看到的只有UI,但在游戲中,除了UI,還有游戲內(nèi)容。而游戲內(nèi)容是什么呢?
舉個(gè)例子:
比如在2D游戲中,除了那些可以交互的按鈕滾動(dòng),在二維場景中的背景、物件、NPC等,都屬于游戲內(nèi)容。在進(jìn)行寬高比適配的時(shí)候,難免會(huì)按照寬度或高度做一些裁剪,如果不進(jìn)行處理,有些游戲內(nèi)容就會(huì)看不到。
在3D游戲中,場景的大小是固定的,當(dāng)相機(jī)照射的寬高比因?yàn)檫m配屏幕寬高比變化時(shí),就可能“穿幫”,很有可能看到黑邊。
在unity中不管2D游戲還是3D游戲,分辨率大小不會(huì)影響游戲內(nèi)容顯示,屏幕寬高比會(huì)影響游戲內(nèi)容顯示。
注意:在unity編輯器中,game視圖是默認(rèn)的視口,并且編輯器下改變游戲?qū)捀弑龋瑄nity會(huì)自動(dòng)把相機(jī)寬高比調(diào)整到適配的寬高比。
unity中常見的適配方式
- Camera組件
Projection:投影類型
Prespective為透視投影
Field of View:相機(jī)的張角,它決定相機(jī)照射的范圍。
Clipping Planes:近裁剪面和遠(yuǎn)裁剪面
Viewport Rect:視口大小,取值為0 ~ 1之間
Orthographic為平行投影
與透視投影不同的是size屬性,它用來調(diào)整攝像機(jī)的大小
orthographicSize:等于相機(jī)高度的一半
注意一下,unity中的單位和像素之間有一個(gè)轉(zhuǎn)換關(guān)系,叫做Pixels To Units
默認(rèn)為100,unity中一個(gè)單位表示圖片的100個(gè)像素。如果游戲屏幕高為800像素,那么換算后高度為 800 / 100 / 2 = 4。
unity沒有直接設(shè)置攝像機(jī)寬度的屬性,也沒有獲取攝像機(jī)寬度的接口,但可以通過高度和寬高比計(jì)算出來。那么計(jì)算寬度如下:
cameraWidth = camera.orthographicSize * 2 * camera.aspect
換算成像素:cameraWidth * 100
cameraHeight = camera.orthographicSize * 2
換算成像素:cameraHeight * 100
相機(jī)的寬高比是unity自動(dòng)設(shè)置為當(dāng)前屏幕寬高比的,所以camera.aspect不需要自己設(shè)置。
-
縮放
在Transform組件上,可以設(shè)置控制物體每個(gè)方向上的縮放比例。
這里寫圖片描述 錨點(diǎn)(相對位置)
目前NGUI,UGUI都有類似的功能,稍后再討論。
游戲內(nèi)容適配
游戲內(nèi)容可以分為兩類
有效內(nèi)容:游戲中一定需要顯示在屏幕上的內(nèi)容
實(shí)際內(nèi)容:包括有效內(nèi)容和為了適配、或其它目的增加的內(nèi)容。
3D游戲中把要么場景做得比正常顯示時(shí)更大一些;要么顯示天空盒子。
2D游戲中也是把背景做得大一些,盡可能讓游戲不出現(xiàn)黑邊。
我們的開發(fā)一般都會(huì)選擇在一個(gè)固定的設(shè)計(jì)分辨率上進(jìn)行,比如常用的iOS豎屏游戲設(shè)計(jì)分辨率640*960,我們就以這個(gè)設(shè)計(jì)分辨率為例。通常情況下,設(shè)計(jì)分辨率尺寸就是我們游戲有效內(nèi)容的尺寸。
orthographicSize設(shè)置為4.8,就可以讓游戲內(nèi)容鋪滿屏幕
這里有一篇文章,里面詳細(xì)講了unity 2D游戲的屏幕適配。
NGUI適配方案
-
UIRoot
NGUI中每一個(gè)UI都是以UIRoot作為根節(jié)點(diǎn),該組件完成了NGUI大體上的適配功能。
UIRoot的幾種縮放方式:
這里寫圖片描述
Flexible:
在該模式下,下面的UI都是以像素為基礎(chǔ),100像素的物體無論在多少分辨率上都是100像素,這就意味著,100像素在分辨率低的屏幕上可能顯示正常,但是在高分辨率上就會(huì)顯得很小。
在該模式下,UIRoot的屬性如下:
Minimum Height:設(shè)置為725時(shí),當(dāng)屏幕高度小于725時(shí),在該屏幕上顯示的樣子和開發(fā)時(shí)一致。
Maximum Height:設(shè)置為1024時(shí),當(dāng)屏幕高度大于1024時(shí),在該屏幕上顯示的樣子和開發(fā)時(shí)一致。
Shrink Portrait UI:當(dāng)是豎屏狀態(tài)時(shí),按寬度來適配。
Adjust by DPI:使用dpi做適配計(jì)算。
補(bǔ)充一下一些關(guān)于屏幕的基本概念
dip:設(shè)備無關(guān)像素
dp:就是dip
px:像素
dpi:像素密度,單位面積上有多少個(gè)像素點(diǎn)
分辨率:寬高兩個(gè)方向上的像素點(diǎn)數(shù),如800*600
屏幕尺寸:屏幕對角線長度
屏幕比例:寬高比
詳細(xì)的轉(zhuǎn)換關(guān)系,去這里看看。
開發(fā)時(shí)的布局:
改變Game視圖大小:
改變Game視圖大小,高度大于725,小于1024,(按剛剛的截圖中設(shè)置的值測試的):
在高度在Minimum和Maximum之間時(shí),UIRoot就不會(huì)對下面的UI縮放了,開發(fā)時(shí)有多少像素在高分辨下也只有那么點(diǎn)像素,所以看起來就變小了。
這個(gè)Minimum和Maximum Height用于你對實(shí)際的屏幕尺寸進(jìn)行限制,如果實(shí)際的屏幕尺寸小于Minimum,那么就相當(dāng)于設(shè)置了“Constrained”模式、Manual Height值設(shè)為Minimum的時(shí)候一樣,同理,如果屏幕尺寸超過了Maximum,那也相當(dāng)于設(shè)置了“Constrained”模式、Manual Height值設(shè)為Maximum的時(shí)候一樣。
以上是在Flexible模式的關(guān)于分辨率的適配,還有一個(gè)是寬高比適配,分兩種情況:
當(dāng)高大于寬的是,也就是豎屏狀態(tài)時(shí)
兩邊被截了
只需要勾上Shrink Portrait UI,就能按照寬度來適配了(因?yàn)槟J(rèn)橫屏狀態(tài),并且默認(rèn)按高度適配,所以在看這段源碼的時(shí)候,它里面的計(jì)算是寬高顛倒的):
當(dāng)寬大于高時(shí),也就是橫屏狀態(tài)時(shí):就需要自己來根據(jù)寬度來調(diào)整縮放。
- 動(dòng)態(tài)的改變適配的高度
public class NewBehaviourScript : MonoBehaviour {
public int ManualWidth = 1280;
public int ManualHeight = 720;
void Awake () {
UIRoot uiRoot = gameObject.GetComponent<UIRoot>();
if (uiRoot != null)
{
if (System.Convert.ToSingle(Screen.height) / Screen.width > System.Convert.ToSingle(ManualHeight) / ManualWidth)
uiRoot.minimumHeight = Mathf.RoundToInt(System.Convert.ToSingle(ManualWidth) / Screen.width * Screen.height);
else
uiRoot.minimumHeight = ManualHeight;
}
}
}
-
利用相機(jī)的camera.orthographicSize
需要知道orthographicSize表示的是相機(jī)高度的一半,前面已經(jīng)講清楚了。我在16 : 9屏幕下開發(fā),并且設(shè)置camera.orthographicSize為1,把Minimum和Maximum設(shè)置為相同
這里寫圖片描述,然后把下面腳本掛在UI相機(jī)上:
public class NewBehaviourScript : MonoBehaviour {
void Awake ()
{
camera.orthographicSize *= 16.0f / 9 / ((float)Screen.width / Screen.height);
}
}
16:9屏幕上正常:
不加上述腳本,在5:4屏幕上,兩邊被裁剪了:
加上上述腳本,在5:4屏幕上就正常了,按照寬度適配:
Constrained:
該模式下,屏幕按照尺寸比例來適配,不管實(shí)際屏幕有多大,NGUI都會(huì)通過合適的縮放來適配屏幕。這樣在高分辨率上顯示的UI就會(huì)被放大,有可能會(huì)模糊。
Content Width:按照該寬度值適配屏幕
Content Height:按照該高度值適配屏幕
Fit選項(xiàng)表示已哪個(gè)值做適配。這兩個(gè)值可以認(rèn)為是事先設(shè)定好的屏幕初始大小和比例。源碼中Fit選項(xiàng)的枚舉值:
public enum Constraint
{
Fit,
Fill,
FitWidth,
FitHeight,
}
如果Fit都沒勾選(Constraint.Fill)
當(dāng)適配寬高比小于實(shí)際寬高比時(shí),就會(huì)按照寬度適配,反之按照高度適配。該情況下可以保證顯示結(jié)果永遠(yuǎn)沒有黑邊,但上下或者左右兩邊可能會(huì)被裁剪。如果勾選了Width(Constraint.FitWidth)
那么就會(huì)在屏幕比例發(fā)生變化時(shí),按照寬度來適配。例如開發(fā)時(shí)用16:9屏幕,運(yùn)行在5:4屏幕上,寬度適配,計(jì)算公式為 activeHeight = manualWidth / (screen.x / screen.y),16 / ( 5 / 4 ) = 12.8 > 9,這樣顯示寬度就全部顯示進(jìn)來了,但是高度上就會(huì)出現(xiàn)黑邊,所以這種情況下要想辦法解決黑邊問題。如果勾選了Height(Constraint.FitHeight)
那么就會(huì)在屏幕比例發(fā)生變化時(shí),按照寬度來適配。activeWidth = manualHeight * (screen.x / screen.y),9 * ( 5 / 4 ) = 11.25 < 16,這樣高度全部顯示進(jìn)來,但在寬度上兩邊被裁剪掉了,顯然這樣更不合適了。如果兩個(gè)都勾選了(Constraint.Fit)
當(dāng)適配寬高比大于實(shí)際寬高比時(shí),就會(huì)按照寬度適配,反之按照高度適配。該情況下可以保證顯示開發(fā)時(shí)能見到的所有內(nèi)容,但是可能上下或左右會(huì)出現(xiàn)黑邊。
下面是UIRoot.cs源碼:
可以清楚的看到NGUI是怎么利用這些值進(jìn)行計(jì)算高度的。
Constrained On Mobiles
前兩種模式的組合,在PC和Mac等桌面設(shè)備上用Flexible模式, 在移動(dòng)設(shè)備上Constrained模式。
- UIAnchor or UIStretch
作為NGUI中一個(gè)組件,但之前做的項(xiàng)目里面好像怎么用,可以把它看做對一個(gè)UI樹中的局部進(jìn)行控制。它們在處理細(xì)節(jié)上很相似,都是先計(jì)算參照對象(這個(gè)參照對象由Insprector的Container指定,如果沒有選擇,就是Camera)的大小Rect,然后根據(jù)參數(shù)UIAnchor(Side,relativeOffset,pixelOffset),UIStretch(Style,relativeSize,initialSize,borderPadding)進(jìn)行調(diào)整,最后設(shè)置對應(yīng)的屬性,只不過UIAnchor設(shè)置的是transform.position,UIStretch設(shè)置的是(width,height)或clipRange等。
UIAnchor組件的視圖:
Container:指定Anchor的參照點(diǎn),如果沒有選擇,則以Camera的pixelRect的區(qū)域?yàn)閰⒄彰?。
Side:錨點(diǎn)位置,有八個(gè)位置可選。
Relative Offset:相對于Camera,或Container的百分比偏移 。
Pixel Offset:像素的偏移。
UIStretch組件的視圖:
大致和UIAnchor差不多,Style屬性有些改動(dòng)如下
Horizontal:橫向拉伸。
Vertical:縱向拉伸。
Both:雙向拉伸,但xy方向上的拉伸比例不同。
BasedOnHeight:雙向拉伸,但xy方向上的拉伸比例相同,且比例基于height。
FillKeepingRatio:雙向拉伸,但xy方向上的拉伸比例相同,比例基于較大者。
FitInternalKeepingRatio:雙向拉伸,但xy方向上的比例相同,比例基于較小者。
具體的可以自己研究一下,但是不建議用這個(gè)兩貨,折騰了一下感覺太麻煩,而且根本沒必要啊,因?yàn)閁IRoot已經(jīng)做了大部分的適配了,那些局部細(xì)節(jié)上的調(diào)整完全可以用UIRect所管理的Anchor來實(shí)現(xiàn),它不是單獨(dú)的組件,比這兩簡單多了,下面就來聊聊它。
- UIRect的Anchor
首先得了解一些UIRect,這里不詳細(xì)聊它,后面會(huì)整理一篇分析NGUI底層的文章,里面有詳細(xì)說它。簡單介紹一下,從NGUI控件的繼承結(jié)構(gòu)上,UIRect是所有weight和panel的基類,管理著rect和anchor,計(jì)算、生成,是一個(gè)抽象類。
拿UISprite舉例:
Type:三種類型,使用錨點(diǎn)、基本控制、完全控制。
Execute:設(shè)置在什么時(shí)候執(zhí)行錨點(diǎn)適配。
Target:參考物體。
Left、Right、Bottom、Top:該控件上下左右邊。
比如,你想某個(gè)按鈕在任何尺寸屏幕上都停留在屏幕上的左邊,可以如下:
16:9屏幕上
錨點(diǎn)設(shè)置如下:UISprite的左右邊界都參考target的左邊
然后5:4屏幕上,UISprite依然在屏幕的左邊了
當(dāng)然其它的weight都可以設(shè)置錨點(diǎn),可以這么說,凡事繼承自UIRect的組件都可以使用該錨點(diǎn)。
UGUI適配方案
終于把NGUI適配說完了,對于UGUI目前沒有深入了解,在場景視圖中可以拖拽錨點(diǎn),設(shè)置錨點(diǎn)區(qū)域,感覺挺簡單的,粗略做個(gè)筆記。
- Canvas Scaler:畫布比例縮放,從整體上對UI進(jìn)行適配控制,和UIRoot有異曲同工之妙,很多參數(shù)名字不一樣,但意思一樣。
ConstantPixelSize:按像素適配
Constant Pixel Size:保持UI元素大小不變,無論屏幕尺寸如何變化,所占像素不變。
Scale Factor:保持大小的比例 。原圖100x100 原始大小1=100x100 原來的2倍大 2=200x200
Reference Pixels Per Unity: 100 Unity里的1單位大小代表100像素
ScaleWithScreenSize:按比例適配
Scale With Screen Size:UI元素大小跟隨屏幕分辨率的大小變化而變化。
Reference Resolution:參考分辨率。
Screen Match Mode:
Match Width Or Height:根據(jù)參考分辨率的高或?qū)挘瑏砜s放UI元素。
Expland:分辨率設(shè)置不會(huì)小于Canvas設(shè)置的分辨率。
Shrink:分辨率不會(huì)大于Canvas設(shè)置的分辨率。
Constant Physical Size:按屏幕物理大小適配
根據(jù)屏幕的PPI信息和ConstantPhysicalSize本身的配置信息,得出一個(gè)“合適”的scaleFactor,以達(dá)到UI在不同PPI設(shè)備上的最終大小都是一致的。
- 錨點(diǎn)
UGUI中錨點(diǎn)有多種“形態(tài)”,當(dāng)錨點(diǎn)是一個(gè)點(diǎn)時(shí),表示該UI大小不變,位置會(huì)隨參考點(diǎn)改變。當(dāng)錨點(diǎn)是一個(gè)矩形區(qū)域時(shí),UI的大小就會(huì)隨該參考區(qū)域改變,當(dāng)然非常靈活,錨點(diǎn)矩形的大小可以隨意設(shè)置,甚至可以在某個(gè)方向長度為0。
寫在最后
以上就是屏幕適配的所有內(nèi)容,主要介紹了屏幕適配的分類:分辨率適配和寬高比適配,按內(nèi)容又分為游戲UI適配和游戲內(nèi)容適配,并給出一些適配方法。然后重點(diǎn)講了NGUI的適配方法,簡單介紹了UGUI,總的來說UGUI和NGUI適配的方案有很多相似的地方,適配的大致方向就是按像素、按比例縮放對全局適配,用錨點(diǎn)來做精細(xì)的控制。對UGUI現(xiàn)在不是很熟,所以寫的很簡單,以后找時(shí)間在詳細(xì)研究一下,再整理出來。