【Unity3D-UGUI原理篇】(二)Canvas Scaler 缩放原理
推薦閱讀
- CSDN主頁
- GitHub開源地址
- Unity3D插件分享
- 簡書地址
- 我的個人博客
- QQ群:1040082875
大家好,我是佛系工程師☆恬靜的小魔龍☆,不定時更新Unity開發(fā)技巧,覺得有用記得一鍵三連哦。
一、前言
這個系列文章,是為了記錄分析UGUI的各種原理:
- UGUI的渲染模式。
- UGUI的縮放計(jì)算。
- UGUI的錨點(diǎn)定位。
- UGUI的層級渲染流程。
- UGUI的事件觸發(fā)原理。
- UGUI的布局自動布局方式。
這是本系列文章的第二篇:
 【Unity3D-UGUI原理篇】(一)Canvas 渲染模式
 【Unity3D-UGUI原理篇】(二)Canvas Scaler 縮放原理
 【Unity3D-UGUI原理篇】(三)RectTransform 組件詳解
 【Unity3D-UGUI原理篇】(四)Event System Manager 事件與觸發(fā)
 【Unity3D-UGUI原理篇】(五)Auto Layout 自動布局
 【Unity3D-UGUI原理篇】(六)使用 UnityEngine.Events 讓程序更靈活、穩(wěn)定
二、Canvas Scaler 縮放原理
我們接著來看UGUI的縮放核心組件Canvas Scaler的詳細(xì)屬性:
 
 Canvas 縮放模式有三種:Constant Pixer Size、Scale With Screen Size、Constant Physical Size
下面就分別進(jìn)行進(jìn)行解析:
2-1、Constant Pixer Size —— 恒定像素
| Scale Factor | 縮放因子 | 
| Reference Pixels Per Uit | 單位面積像素?cái)?shù)量,默認(rèn)100. | 
這種模式下 UI以像素為大小,同樣的像素在不同的分辨率下進(jìn)行尺寸的自適應(yīng)。
Scale Factor參數(shù)
首先,來看官方代碼對于這個參數(shù)的設(shè)置:
protected void SetScaleFactor(float scaleFactor) {if (scaleFactor == m_PrevScaleFactor)return;m_Canvas.scaleFactor = scaleFactor;m_PrevScaleFactor = scaleFactor; }用代碼可以看出來,Canvas Scaler 透過設(shè)定Canvas下的Scale Factor參數(shù)來縮放所有在此Canvas下的UI元素的大小,下面就舉個例子說明一下。
例子:
 將Scale Factor設(shè)為1:
 
 Canvas的長寬等于整成的屏幕的長寬(1960 X 1080),縮放是1倍。
 
 
 圖片也是正常大小。
將Scale Factor設(shè)為2:
 
 Canvas的長寬是原來長寬的一半(980 X 540),然后縮放是2倍。
 
 UI也是兩倍大小:
 
 當(dāng)Scale Factor為2時,Scale Factor 會調(diào)整整個Canvas 的大小,并讓他的大小跟屏幕大小一樣,運(yùn)算后Canvas Size放大2倍,剛好等于屏幕大小,而Canvas往下的子對象ImageI會放大2倍。
Reference Pixels Per Unit參數(shù)
這里,我們詳細(xì)的介紹一下Constant Pixer Size模式下的Reference Pixels Per Unit屬性跟圖片的Pixels Per Unit的關(guān)系。
舉個例子
 導(dǎo)入項(xiàng)目一張圖片,將圖片的Pixels Per Unit設(shè)置為100:
 
 場景中有一個標(biāo)準(zhǔn)大小的Cube和一個標(biāo)準(zhǔn)大小的Sprite,兩者的縮放比例都為1,大小一致:
 
 當(dāng)圖片的Pixels Per Unit為100,每單位由100Pixels組成,那么這個Sprite在世界坐標(biāo)就是
圖片大小 = (100/100) * (100/100)=1 Unit
然后,將圖片的Pixels Per Unit設(shè)置為10:
 
圖片大小 = (100/10) * (100/10)=10 Unit
結(jié)論:
- Unity中一單位等于 100 Pixels
- 公式:Sprite的大小 = 原圖大小(Pixels)/ Pixels Per Unit
讓我們回到 Reference Pixels Per Unit,官方解釋是,如果圖片檔有設(shè)定Pixels Per Unit,則會將Sprite 的 1 pixel 轉(zhuǎn)換成 UI 中的 1 pixel,讓我們來看一下官方代碼:
public float pixelsPerUnit {get{float spritePixelsPerUnit = 100;if (sprite)spritePixelsPerUnit = sprite.pixelsPerUnit;float referencePixelsPerUnit = 100;if (canvas)referencePixelsPerUnit = canvas.referencePixelsPerUnit;return spritePixelsPerUnit / referencePixelsPerUnit;} } public override void SetNativeSize() {if (overrideSprite != null){float w = overrideSprite.rect.width / pixelsPerUnit;float h = overrideSprite.rect.height / pixelsPerUnit;rectTransform.anchorMax = rectTransform.anchorMin;rectTransform.sizeDelta = new Vector2(w, h);SetAllDirty();} }上面的代碼,可以看出 Image 是通過 spritePixelsPerUnit / referencePixelsPerUnit 方式算出新的 pixelsPerUnit大小。
在設(shè)定 Image 圖片大小時,是把 寬高 / Pixels Per Unit。
實(shí)測一下,建立一個Canvas參數(shù)如下:
 
 Canvas下面新建一個Image,參數(shù)如下:
 
 通過修改Canvas Scaler的Reference Pixels Per Unit參數(shù) 與 圖片的Pixels Per Unit參數(shù),來做4組測試,然后來看圖片的不同變化:
| 100 | 100 | 100*100 | 
| 200 | 100 | 200*200 | 
| 100 | 10 | 1000*1000 | 
| 200 | 10 | 2000*2000 | 
■ 上表可以看出當(dāng)數(shù)值改變時,圖片預(yù)設(shè)大小也會改變
■ 由此可以推導(dǎo)出公式
UI大小 = 原圖大小(Pixels) / (Pixels Per Unit / Reference Pixels Per Unit)
2-2、Scale With Screen Size —— 屏幕尺寸比例
| Referencee Resolution | 預(yù)設(shè)屏幕大小 | 
| Screen Match Mode | 縮放模式 | 
| Match | 寬高比 | 
這種縮放模式下的UI位置是根據(jù)屏幕的分辨率和設(shè)置的寬高比來調(diào)整UI的位置的,通常做屏幕UI自適應(yīng)的時候都需要調(diào)整到這個縮放模式下。
先來看官方的算法:
Vector2 screenSize = new Vector2(Screen.width, Screen.height);float scaleFactor = 0; switch (m_ScreenMatchMode) {case ScreenMatchMode.MatchWidthOrHeight:{// We take the log of the relative width and height before taking the average.// Then we transform it back in the original space.// the reason to transform in and out of logarithmic space is to have better behavior.// If one axis has twice resolution and the other has half, it should even out if widthOrHeight value is at 0.5.// In normal space the average would be (0.5 + 2) / 2 = 1.25// In logarithmic space the average is (-1 + 1) / 2 = 0float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);break;}case ScreenMatchMode.Expand:{scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);break;}case ScreenMatchMode.Shrink:{scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);break;} }下面,就詳細(xì)的說一下Screen Match Mode縮放模式:
Expand(擴(kuò)大):將Canvas Size進(jìn)行寬或高擴(kuò)大
根據(jù)官方代碼,計(jì)算方式如下:
scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
意思是分別算出長寬 ,也就是Screen Size在Reference Resolution的比例。
舉例來說,Reference Resolution為1280 X 720,Screen Size為800 X 600
ScaleFactor Width: 800/1280=0.625
 ScaleFactor Height:600/720=0.83333
套用ScaleFactor公式:
Canvas Size = Screen Size / Scale Factor
 Canvas Width:800 / 0.625 = 1280
 Canvas Height:600 / 0.625 = 960
Canvas Size 為 1280*960,高度從720變成了960,最大程度的放大(顯示所有元素)
 
Shrink(收縮):將Canvas Size進(jìn)行寬或高收縮
根據(jù)官方代碼,計(jì)算方式如下:
scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
意思是分別算出長寬 ,也就是Screen Size在Reference Resolution的比例。
舉例來說,Reference Resolution為1280 X 720,Screen Size為800 X 600
ScaleFactor Width: 800/1280=0.625
 ScaleFactor Height:600/720=0.83333
套用ScaleFactor公式:
Canvas Size = Screen Size / Scale Factor
 Canvas Width:800 / 0.625 = 960
 Canvas Height:600 / 0.83333 = 720
Canvas Size 為 960*720,寬度從1280變成了960,最大程度的縮小
 
Match Width or Height:根據(jù)Width或Height進(jìn)行混合縮放
根據(jù)官方代碼,計(jì)算方式如下:
float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase); float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase); float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight); scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);分別對ScaleFactor Width、Height取對數(shù)后,再進(jìn)行平均混合,那為什麼不直接使用March對Width、Height進(jìn)行混合呢??,讓我們來比較一下
假設(shè)Reference Resolution為400 X 300,Screen Size為200 X 600 大小關(guān)系是
Reference Resolution Width 是 Screen Size Width的2倍
 Reference Resolution Height 是 Screen Size 的0.5倍
看起來會像下圖:
 
 當(dāng)March為0.5時,ScaleFactor應(yīng)該要是 1 (拉平):
ScaleFactor Width: 200/400=0.5
 ScaleFactor Height:600/300=2
一般混合:
ScaleFactor = March * ScaleFactor Width + March * ScaleFactorHeight
 ScaleFactor = 0.5 * 0.5 + 0.5 * 2 = 1.25
對數(shù)混合:
logWidth:log2(0.5) = -1
 logHeight:log2(2) = 1
 logWeightedAverage:0
 ScaleFactor:20 = 1
scaleFactor一般混合為1.25,對數(shù)混合為1,結(jié)果很明顯,使用對數(shù)混合能更完美的修正大小。
2-3、Constant Physical Size —— 恒定尺寸
 屬性說明:
1、Physical Unit單位
| Centimeters | 公分(cm、厘米) | 2.54 | 
| Millimeters | 公厘(mm,毫米) | 25.4 | 
| Inches | 英寸 | 1 | 
| Points | 點(diǎn) | 72 | 
| Picas | 皮卡 | 6 | 
2、Fallback Screen DPI:備用Dpi,當(dāng)找不到設(shè)備Dpi時,使用此值
 3、Default Sprite DPI:預(yù)設(shè)的圖片Dpi
官方代碼:
float currentDpi = Screen.dpi; float dpi = (currentDpi == 0 ? m_FallbackScreenDPI : currentDpi); float targetDPI = 1; switch (m_PhysicalUnit) {case Unit.Centimeters: targetDPI = 2.54f; break;case Unit.Millimeters: targetDPI = 25.4f; break;case Unit.Inches: targetDPI = 1; break;case Unit.Points: targetDPI = 72; break;case Unit.Picas: targetDPI = 6; break; }SetScaleFactor(dpi / targetDPI); SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit * targetDPI / m_DefaultSpriteDPI);結(jié)論
■ ScaleFactor 為 “目前硬體dpi” 佔(zhàn)了 “目標(biāo)單位” 的比例
■ ReferencePixelsPerUnit 要與目前的Dpi在運(yùn)算求出新的值,再傳入Canvas中求出大小,公式如下:
新的 Reference Pixels Per Unit = Reference Pixels Per Unit * Physical Unit / Default Sprite DPI
UI大小 = 原圖大小(Pixels) / (Pixels Per Unit / 新的 Reference Pixels Per Unit)
總結(jié)
以上是生活随笔為你收集整理的【Unity3D-UGUI原理篇】(二)Canvas Scaler 缩放原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 阿里云短信调用
- 下一篇: linux与python客户端,Pyth
