UGUI中Canvas Scaler的功能说明

在工作中用到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单位,我认为有这样一个名词对于理解接下来的内容有很大帮助。

UGUI中Canvas Scaler的功能说明_第1张图片

有了上面的定义之后,我们可以得到:当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的功能说明)