Unity NGUI屏幕適配

前段時(shí)間整理的一篇關(guān)于unity ui開發(fā)的文章,被推薦上了csdn首頁,對于剛剛寫文字的我來說,是莫大的鼓勵(lì),讓我干勁十足,寫出更多有質(zhì)量的文字。

寫在前面

屏幕適配是每個(gè)手機(jī)應(yīng)用和游戲都會(huì)解決的問題,當(dāng)然在開發(fā)的過程中會(huì)遇到各種各樣的坑,這次,我們就來討論一下unity項(xiàng)目中的屏幕適配吧!

目錄

  1. 屏幕適配的分類
  2. 哪些內(nèi)容需要適配
  3. unity中常見的適配方式
  4. 游戲內(nèi)容適配
  5. NGUI的適配方案
  6. UGUI的適配方案

屏幕適配的分類

說到屏幕適配的分類啊,也許會(huì)有所疑問,屏幕適配還能分類?細(xì)致分析一下,可以分為兩大類:分辨率適配和寬高比適配。

  1. 分辨率適配
    首先得知道分辨率是什么?分辨率是屏幕顯示圖像的緊密度,是指顯示器能顯示的像素有多少。屏幕上的點(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)該做的。

  2. 寬高比適配
    這個(gè)很好理解,每個(gè)手機(jī)大小各不相同,寬高比也會(huì)有多種啦,適配寬高比當(dāng)然也是必須要做的嘍。

當(dāng)下移動(dòng)設(shè)備主流分辨率及寬高比:
iOS設(shè)備的分辨率主要有:


這里寫圖片描述

Android設(shè)備的分辨率則相對紛雜,主流的分辨率有:


這里寫圖片描述

哪些內(nèi)容需要適配

  1. 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就變得很小了,影響美觀,影響用戶正常使用。

這里寫圖片描述
  1. 游戲內(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中常見的適配方式

  1. 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è)置。

  1. 縮放
    在Transform組件上,可以設(shè)置控制物體每個(gè)方向上的縮放比例。


    這里寫圖片描述
  2. 錨點(diǎn)(相對位置)
    目前NGUI,UGUI都有類似的功能,稍后再討論。

游戲內(nèi)容適配

游戲內(nèi)容可以分為兩類
有效內(nèi)容:游戲中一定需要顯示在屏幕上的內(nèi)容
實(shí)際內(nèi)容:包括有效內(nèi)容和為了適配、或其它目的增加的內(nèi)容。

  1. 3D游戲中把要么場景做得比正常顯示時(shí)更大一些;要么顯示天空盒子。

  2. 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適配方案

  1. 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模式。

  1. 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ú)的組件,比這兩簡單多了,下面就來聊聊它。

  1. 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è)筆記。

  1. 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è)備上的最終大小都是一致的。

  1. 錨點(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ì)研究一下,再整理出來。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容