Unity UGUI 原理篇(二):Canvas Scaler 縮放核心

转载http://www.arkaistudio.com/blog/2016/03/28/unity-ugui-%e5%8e%9f%e7%90%86%e7%af%87%e4%ba%8c%ef%bc%9acanvas-scaler-%e7%b8%ae%e6%94%be%e6%a0%b8%e5%bf%83/

目標

  • 了解各種不同 UI Scale Mode
  • Pixels Per Unit 每單位像素
  • Canvas Scale Factor 縮放因子
  • Reference Resolution(預設螢幕大小)、Screen Size、Canvas Size 之間的關係與算法

 

 

本系列其他文章

 

使用環境 與 版本

  • Window 7
  • Unity 5.2.4

 

Canvas Scaler

Canvas Scaler 是 Unity UI 系統中,控制UI元素的總體大小和像素密度的 Compoent,Canvas Scaler 的縮放比例影響著 Canvas 下的元素,包含字體大小和圖像邊界。

 

Size

Reference Resolution:預設螢幕大小

Screen Size:目前螢幕大小

size-1

 

Canvas Size:Canvas Rect Transform 寬高

size-2

 

Scale Factor

http://docs.unity3d.com/ScriptReference/Canvas-scaleFactor.html

用於縮放整個Canvas,而且調整Canvas Size與Screen Size一樣

 

先來看一段官方程式碼

CanvasScaler.cs

C#

1

2

3

4

5

6

7

8

protected void SetScaleFactor(float scaleFactor)

{

    if (scaleFactor == m_PrevScaleFactor)

        return;

 

    m_Canvas.scaleFactor = scaleFactor;

    m_PrevScaleFactor = scaleFactor;

}

程式碼可以看出,Canvas Scaler 透過設定Canvas下的Scale Factor,縮放所有在此Canvas下的元素

 

當Scale Factor為1時,Screen Size (800*600)、Canvas Size(800*600),圖片大小1倍

Scale Factor-1

Scale Factor-1-1

 

當Scale Factor為2時,Screen Size (800*600)、Canvas Size(400*300),圖片大小2倍

Scale Factor-2

Scale Factor-2-1

在當Scale Factor為2時,Scale Factor 會調整整個Canvas 的大小,並讓他的大小跟Screen Size一樣,運算後Canvas Size放大2倍,剛好等於Screen Size,而底下的圖片會放大2倍

 

UI Scale Mode

Constant Pixel Size

Canvas Size 始終等於 Screen Size,透過Scale Factor直接縮放所有UI元素

 

1. Scale Factor:透過此Factor縮放所有在此Canvas下的元素

2. Reference Pixels Per Unit:

先介紹圖片檔設定中的Pixels Per Unit,意思是在這張Sprite中,世界座標中的一單位由幾個Pixel組成

 Pixels Pre Unit-1

 

這邊使用的測試圖片為原始大小100*100 的圖檔,這邊統稱測試圖

 Test Image

 

舉例來說,場景中有一個1*1 Cube ,與一個Sprite圖片指定為測試圖,兩者的Transform Scale 都為 1

當 Pixels Per Unit=100,每單位由 100 Pixel組成,Sprite 是100*100 Pixels,那 Sprite 在世界座標中大小就會變成 100/100 * 100/100 = 1*1 Unit

Pixels Pre Unit-2

(左:Cube ,右:Sprite)

 

當 Pixels Per Unit=10,每單位由 10 Pixel組成,Sprite 是100*100 Pixels,那 Sprite 在世界座標中大小就會變成 100/10 * 100/10 = 10*10 Unit

Pixels Pre Unit-3

(左:Cube,右:Sprite)

 

結論:

  • Unity中一單位等於 100 Pixels
  • 由此可以推導出公式

Sprite 在世界座標中大小 = 原圖大小(Pixels) / Pixels Per Unit

 

讓我們回到 Reference Pixels Per Unit,官方解釋是,如果圖片檔有設定Pixels Per Unit,則會將Sprite 的 1 pixel 轉換成 UI 中的 1 pixel

Image.cs

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public float pixelsPerUnit

{

    get

    {

        float spritePixelsPerUnit = 100;

        if (sprite)

            spritePixelsPerUnit = sprite.pixelsPerUnit;

 

        float referencePixelsPerUnit = 100;

        if (canvas)

            referencePixelsPerUnit = canvas.referencePixelsPerUnit;

 

        return spritePixelsPerUnit / referencePixelsPerUnit;

    }

}

上面官方程式碼,可以看出 Image 透過 spritePixelsPerUnit / referencePixelsPerUnit 方式算出新的 pixelsPerUnit

 

Image.cs

C#

1

2

3

4

5

6

7

8

9

10

11

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 圖片大小時,是把 寬高 / pixelsPerUnit

 

實作一下,建立一個Canvas參數如下

Reference Pixels Per Unit-1

 

Canvas底下建立一個Image,Sprite設定為測試圖,參數如下

Reference Pixels Per Unit-2

 

這邊做4種不同的測試:測試方式是修改 Reference Pixels Per Unit 與 Pixels Per Unit 後,點下 Image Compoent 的 Set Native Size來設定圖片原始大小,藉此看到圖片變化

Reference Pixels Per UnitPixels Per UnitImage  Rect Transform(w*h)
100100100*100
200100200*200
100101000*1000
200102000*2000
  • 上表可以看出當數值改變時,圖片預設大小也會改變
  • 由此可以推導出公式

UI大小 = 原圖大小(Pixels)  /  (Pixels Per Unit / Reference Pixels Per Unit)

 

Scale With Screen Size

透過設定的Reference Resolution(預設螢幕大小)來縮放

  • Reference Resolution:預設螢幕大小
  • Screen Match Mode:縮放模式

先來看官方的算法

CanvasScaler.cs

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

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 = 0

        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);

        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;

    }

}

 

 

a. Expand(擴大):將Canvas Size進行寬或高擴大,讓他高於Reference Resolution,計算如下

1

scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);

意思是分別算出長寬 ,”Screen Size” 佔了 “Reference Resolution” 的比例,在求小的

舉例來說,Reference Resolution為1280*720,Screen Size為800*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,最大程度的放大(顯示所有元素)

 

 

b. Shrink(收縮):將Canvas Size進行寬或高收縮,讓他低於Reference Resolution,計算如下

1

scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);

意思是分別算出長寬 ,”Screen Size” 佔了 “Reference Resolution” 的比例,在求大的

舉例來說,Reference Resolution為1280*720,Screen Size為800*600

ScaleFactor Width: 800/1280=0.625

ScaleFactor Height:600/720=0.83333

 

套用ScaleFactor公式:Canvas Size = Screen Size / Scale Factor

Canvas Width:800 / 0.83333 = 960

Canvas Height:600 / 0.83333 = 720

Canvas Size 為 960*720,寬度從1280變成了960,最大程度的縮小

 

 

c. Match Width or Height:根據Width或Height進行混合縮放,計算如下

1

2

3

4

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取對數後,再進行平均混合,那為什麼不直接使用Match對Width、Height進行混合呢??,讓我們來比較一下

假設Reference Resolution為400*300,Screen Size為200*600 大小關係是

Reference Resolution Width 是 Screen Size Width的2倍

Reference Resolution Height 是 Screen Size 的0.5倍

看起來會像下圖

 

當Match為0.5時,ScaleFactor應該要是 1 (拉平)

ScaleFactor Width: 200/400=0.5

ScaleFactor Height:600/300=2

一般混合:

ScaleFactor = Match * ScaleFactor Width + Match * ScaleFactorHeight

ScaleFactor = 0.5 * 0.5 + 0.5 * 2 = 1.25

對數混合:

logWidth:log2(0.5) = -1

logHeight:log2(2) = 1

logWeightedAverage:0

ScaleFactor:20 = 1

scaleFactor一般混合為1.25,對數混合為1,結果很明顯,使用對數混合能更完美的修正大小

 

 

Constant Physical Size

透過硬體設備的Dpi(Dots Per Inch 每英吋點數),進行縮放

 

1. Physical Unit:使用的單位種類

單位種類中文與1英吋關係
Centimeters公分(cm,厘米)2.54
Millimeters公釐(mm,毫米)25.4
Inches英吋1
Points72
Picas皮卡(十二點活字)6

2. Fallback Screen DPI:備用Dpi,當找不到設備Dpi時,使用此值

3. Default Sprite DPI:預設的圖片Dpi

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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);

 

結論:

  • ScaleFactor 為 “目前硬體dpi” 佔了 “目標單位” 的比例
  • ReferencePixelsPerUnit 要與目前的Dpi在運算求出新的值,再傳入Canvas中求出大小,公式如下:

新的 Reference Pixels Per Unit = Reference Pixels Per Unit * Physical Unit / Default Sprite DPI

UI大小 = 原圖大小(Pixels)  /  (Pixels Per Unit / 新的 Reference Pixels Per Unit)

 

參考資料

 

歡迎轉載,並註明出處 !

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值