UGUI中Canvas Scaler的功能说明

作者: 看到了是吧 | 来源:发表于2017-12-20 10:29 被阅读71次

    在工作中用到Canvas Scaler组件时,只使用到了UIScaleMode为ScaleWithScreenSize选项的情况。今天把这块的源码仔细看了一下,并且参考了这篇GAD的文章,终于弄懂了一些,下面分享一下自己的学习心得。

    tips:建议读者也阅读一下上文提到的文章,因为这篇文章没有分析过程。

    CanvasScaler是一个控制Canvas缩放的组件,共分为以下四种模式:

    1. Constant Pixel Size 固定像素大小,此时使用ScaleFactor值直接控制Canvas的Scale值。
    2. Scale With Screen Size 按照屏幕大小进行缩放,会根据Resolution大小与当前屏幕大小以及三种缩放策略控制Canvas的Scale值。
    3. Constant Physical Size 固定物理大小,此时根据设置的物理单位直接控制Canvas的Scale值。
    4. World 当Canvas的RenderMode为WorldSpace时会显示这种模式,此时CanvasScaler不能控制Canvas的缩放。

    在详细说明四种模式之前,我想先提出一个概念:UGUI单位(或简称UI单位)。UI单位指的是你在设置UI组件大小时,所使用的单位。
    例如下图中,我会把960和640的单位称为UI单位,我认为有这样一个名词对于理解接下来的内容有很大帮助。

    有了上面的定义之后,我们可以得到:当UI组件的根Canvas的Scale为1时,1UI单位的大小为1像素。

    Const Pixel Size 固定像素大小

    在这种模式下,我们的UI组件在不同大小的屏幕上,会占用相同数量的像素。当ScaleFactor的值为x时,一个(100,100)的Image,在任何屏幕上,都会占用100x*100x 个像素,即1UI单位等于x个像素。
    在采用这种模式的情况下,同样的UI在手机上会比在电脑上小很多,因为手机屏幕的像素密度要远远大于电脑(即手机屏幕的DPI大于电脑屏幕)

    Constant Physical Size 固定物理大小

    在这种模式下,我们的UI组件在不同大小的屏幕上,会占用相同大小的物理长度。假如我们设置PhysicalUnit的值为Inches,则我们的Canvas的Scale值会被设置为96(我电脑屏幕的DPI为96),此时1UI单位等于96个像素(即1英寸)。
    举例来说,当你有一张你手掌的图片,你希望在任意的设备上,这张手掌图片都可以和你的手掌完全重合,可以采用这种模式。

    这种模式下的两个参数:Fallback Screen DPI和DefaultSpriteDPI,是Unity无法取得屏幕的DPI值时的默认值和Sprite DPI默认值。当无法取得当前设备屏幕时,使用FallbackScreenDPI,而Sprite DPI是描述你使用图片的DPI,对于这个值我现在不能很好的理解,目前我认为这个值与FallbackScreenDPI设置为相同的比较好,如果有错误的话还希望告知。
    实现代码如下:

            protected virtual void HandleConstantPhysicalSize()
            {
                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);
            }
    

    Scale With Screen Size 根据屏幕大小缩放

    在这种模式下,Canvas的Scale值会根据屏幕的实际大小与预设屏幕大小的比例进行缩放,缩放的策略分为三种:

    1. 根据长宽的Match值进行调整
    2. 使用长宽中最小的比例进行缩放
    3. 使用长宽中最大的比例进行缩放

    具体的实现代码如下:

            protected virtual void HandleScaleWithScreenSize()
            {
                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;
                    }
                }
    
                SetScaleFactor(scaleFactor);
                SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit);
            }
    

    World 世界模式

    World模式下不能设置Canvas的Scale值,此时能设置的一个值为Dynamic Pixels Per Unit。这个值目前看来是控制Text在世界UI下时显示的清晰度的,该值越大,相同Text显示的越清晰。应该说Unity在这里的实现比较诡异的(在我的理解中不应该放在Canvas Scaler里面)。


    另外,所有的模式下都有一个可设置的值叫做Reference Pixels Per Unit。这个值是用来描述在NativeSize下1UI单位,应该对应多少Sprite中的像素的。
    例如 Reference Pixels Per Unit 设置为200 则一个100x100的图片放入Image组件,点击SetNativeSize之后,得到的长宽是(200,200)
    具体代码如下:

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

    相关文章

      网友评论

        本文标题:UGUI中Canvas Scaler的功能说明

        本文链接:https://www.haomeiwen.com/subject/jynwwxtx.html