https://blog.csdn.net/gz_huangzl/article/details/52484611

Canvas Scaler

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

Size

Reference Resolution:預設螢幕大小

Screen Size:目前螢幕大小

Canvas Size:Canvas Rect Transform 寬高

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為2時,Screen Size (800*600)、Canvas Size(400*300),圖片大小2倍

在當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組成

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

舉例來說,場景中有一個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

(左:Cube ,右:Sprite)

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

(左: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參數如下

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

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

Reference Pixels Per Unit Pixels Per Unit Image  Rect Transform(w*h)
100 100 100*100
200 100 200*200
100 10 1000*1000
200 10 2000*2000

■ 上表可以看出當數值改變時,圖片預設大小也會改變

■ 由此可以推導出公式

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

Scale With Screen Size:

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

1. Reference Resolution:預設螢幕大小

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

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

Reference Resolution Width 是 Screen Size Width的2倍

Reference Resolution Height 是 Screen Size 的0.5倍

看起來會像下圖

當March為0.5時,ScaleFactor應該要是 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

對數混合:

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
Points 72
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)

參考資料

■ Unity – Manual: Canvas

http://docs.unity3d.com/Manual/class-Canvas.html

Unity UGUI 原理篇(二):Canvas Scaler 縮放核心的更多相关文章

  1. Unity UGUI——UI基础,Canvas

    主题:画布--Canvas 内容:创建Canvas UI控件的绘制顺序 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTXJfQUhhbw==/font/5 ...

  2. Unity ugui屏幕适配与世界坐标到ugui屏幕坐标的转换

    我们知道,如今的移动端设备分辨率五花八门,而开发过程中往往只取一种分辨率作为设计参考,例如采用1920*1080分辨率作为参考分辨率. 选定了一种参考分辨率后,美术设计人员就会固定以这样的分辨率来设计 ...

  3. Unity UGUI之Canvas&EventSystem

    最近想写一套关于UGUI所有控件的基础使用教程系列,主要是根据本人的使用心得来写的,所以其中可能难以避免会有不正确的地方. 好了进入主题,既然是第一篇,我觉得我有必要先介绍一下UGUI必不可缺的两个组 ...

  4. Unity UGUI图文混排源码(二)

    Unity UGUI图文混排源码(一):http://blog.csdn.net/qq992817263/article/details/51112304 Unity UGUI图文混排源码(二):ht ...

  5. 【转】Android LCD(二):LCD常用接口原理篇

    关键词:android LCD TFT TTL(RGB)  LVDS  EDP MIPI  TTL-LVDS  TTL-EDP 平台信息:内核:linux2.6/linux3.0系统:android/ ...

  6. Android LCD(二):LCD常用接口原理篇(转)

    源: Android LCD(二):LCD常用接口原理篇

  7. Curved UI - VR Ready Solution To Bend Warp Your Canvas 1.7,1.8,2.2,2.3 四种版本压缩包(Unity UGUI曲面插件),可以兼容VRTK

    Curved UI - VR Ready Solution To Bend Warp Your Canvas 1.7,1.8,2.2,2.3 四种版本压缩包(Unity UGUI曲面插件) 可以兼容V ...

  8. 【Unity笔记】关于UGUI的根节点Canvas

    创建UGUI物体时,会自动创建Canvas物体作为所有UGUI的根节点.该物体身上有个Canvas脚本,Render Mode渲染模式选项: Screen Space - Overlay:即使场景中没 ...

  9. Esfog_UnityShader教程_遮挡描边(原理篇)

    咳咳,有段时间没有更新了,最近有点懒!把不少精力都放在C++身上了.闲言少叙,今天要讲的可和之前的几篇有所不同了,这次是一个次综合应用.这篇内容中与之前不同主要体现在下面几点上. 1.之前我们写的都是 ...

随机推荐

  1. LTE流程

    LTE 过程全流程 1. UE处于关闭状态 2. 打开UE电源. 3. 搜索附近的频率 4. 同步时间 5. 小区搜索 6. 小区选择 7. 解码MIB 8. 解码SIB 9. 初始化RACH过程 1 ...

  2. Canvas 与 SVG 的比较

    Canvas:<canvas> 标签定义图形(只是图形容器),比如图表和其他图像,您必须使用脚本 (通常是JavaScript)来绘制图形.默认情况下 <canvas> 元素没 ...

  3. 如何设置select下拉禁止选择

    转自:https://blog.csdn.net/you23hai45/article/details/52233207

  4. jdbc.properties 文件的配置

    jdbc.properties文件的配置   使用配置文件访问数据库的优点是: 一次编写随时调用,数据库类型发生变化只需要修改配置文件. 配置文件的设置: 在配置文件中,key-value对应的方式编 ...

  5. HBase入门基础教程 HBase之单机模式与伪分布式模式安装

    在本篇文章中,我们将介绍Hbase的单机模式安装与伪分布式的安装方式,以及通过浏览器查看Hbase的用户界面.搭建HBase伪分布式环境的前提是我们已经搭建好了Hadoop完全分布式环境,搭建Hado ...

  6. springmvc 初始化参数绑定(使用属性编辑器) 来处理类型转换问题

    处理一种日期格式 处理器中的写法: index.jsp中的写法: 处理多种日期格式: 处理器的写法: 自定义的属性编辑器: index.jsp的写法:

  7. [poj2318]TOYS(直线与点的位置关系)

    解题关键:计算几何入门题,通过叉积判断. 两个向量的关系: P*Q>0,Q在P的逆时针方向: P*Q<0,Q在P的顺时针方向: P*Q==0,Q与P共线. 实际就是用右手定则判断的. #i ...

  8. 用于.NET环境的时间测试(转)

    用于.NET环境的时间测试   在.NET环境中,衡量运行完整算法所花费的时间长度,需要考虑很多 需要考虑很多种情况 ,如:程序运行所处的线程以及无用单位收集(GC垃圾回收). 在程序执行过程中无用单 ...

  9. Codeforces 914C Travelling Salesman and Special Numbers (数位DP)

    题意:题目中定义了一种运算,把数字x变成数字x的二进制位数.问小于n的恰好k次运算可以变成1的数的个数(题目中的n是二进制数,n最大到2^1000) 思路:容易发现,无论多么大的数,只要进行了一次运算 ...

  10. Codeforces #536 div2 E (1106E)Lunar New Year and Red Envelopes (DP)

    题意:过年了,Bob要抢红包.抢红包的时间段为1 - n,有m个红包,每个红包有三个属性:st(红包出现的时间), ed(红包消失的时间),d(如果抢了这个红包,能够抢下一个红包的时间),w(红包的收 ...