This is the 14th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

Recommended reading

  • CSDN home page
  • GitHub open source address
  • Unity3D plugin sharing
  • Jane’s address book
  • My personal blog
  • QQ group: 1040082875

Hello everyone, I am a Buddhist engineer ☆ quiet small magic dragon ☆, update Unity development skills from time to time, think useful remember one key three link oh.

One, foreword

The purpose of this series of articles is to document and analyze the various principles of UGUI:

  • UGUI rendering mode.
  • UGUI zoom calculation.
  • UGUI anchor location.
  • UGUI hierarchy rendering process.
  • UGUI event triggering principle.
  • UGUI layout automatic layout mode.

2. Canvas Scaler scaling principle

Let’s move on to the scaling core component of UGUICanvas ScalerDetailed properties of:There are three Canvas scaling modes: Constant Pixer Size, Scale With Screen Size, and Constant Physical Size

The following are analyzed respectively:

2-1, Constant Pixer Size — Constant pixel

attribute function
Scale Factor The zoom factor
Reference Pixels Per Uit Number of pixels per area, default 100.

In this mode, the UI is sized in pixels, and the same pixels are sized at different resolutions.

Scale Factor parameters

First, let’s look at how the official code sets this parameter:

protected void SetScaleFactor(float scaleFactor)
{
    if (scaleFactor == m_PrevScaleFactor)
        return;
 
    m_Canvas.scaleFactor = scaleFactor;
    m_PrevScaleFactor = scaleFactor;
}
Copy the code

As can be seen from the code, Canvas Scaler scales all UI elements under the Canvas by setting the Scale Factor parameter under the Canvas. Here is an example to illustrate.

Example: Set Scale Factor to 1: The length and width of the Canvas is equal to the length and width of the entire screen (1960 X 1080), and the zoom is 1 times. The picture is also normal size.

Set Scale Factor to 2: The Canvas is half the length and width of the original Canvas (980 X 540) and then zoomed in by 2 times.The UI is also twice the size:When Scale Factor is 2, Scale Factor will adjust the Size of the entire Canvas and make it the same Size as the screen. After calculation, the Canvas Size will be enlarged by 2 times, which is exactly equal to the screen Size, and the child object ImageI below the Canvas will be enlarged by 2 times.

Reference Pixels Per Unit parameter

Here, we introduce in detail the relationship between the Reference Pixels Per Unit attribute in Constant Pixer Size mode and the image Pixels Per Unit.

For exampleCreate Pixels Per Unit (Pixels Per Unit); Import Pixels Per Unit (Pixels Per Unit)The scene has a standard-sized Cube and a standard-sized Sprite, both with a scale of 1 and the same size:When the image’s Pixels Per Unit is 100, the world coordinates of this Sprite are

Picture size = (100/100) * (100/100)=1 Unit

Next, set the Pixels Per Unit of your image to 10:

Picture size = (100/10) * (100/10)=10 Unit

Conclusion:

  • A unit in Unity is equal to 100 Pixels
  • Sprite size = Pixels/Pixels Per Unit

In Pixels Per Unit, a pixel in a Sprite is converted into a pixel in the UI.

public float pixelsPerUnit
{
    get
    {
        float spritePixelsPerUnit = 100;
        if (sprite)
            spritePixelsPerUnit = sprite.pixelsPerUnit;
 
        float referencePixelsPerUnit = 100;
        if (canvas)
            referencePixelsPerUnit = canvas.referencePixelsPerUnit;
 
        returnspritePixelsPerUnit / 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 = newVector2(w, h); SetAllDirty(); }}Copy the code

From the above code, it can be seen that Image calculates the new pixelsPerUnit size through spritePixelsPerUnit/referencePixelsPerUnit.

Pixels Per Unit = width/height/Pixels Per Unit

To test this, create a Canvas parameter as follows:Create a new Image under Canvas with the following parameters:4 sets of tests are performed by modifying the Reference Pixels Per Unit parameter of Canvas Scaler and the Pixels Per Unit parameter of image. Then, see the different changes of images:

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

■ As you can see from the above table, the preset size of the image changes when the value changes

■ The formula can be derived from this

UI size = Pixels/(Pixels Per Unit/Reference Pixels Per Unit)

2-2, Scale With Screen Size — Scale With Screen Size

attribute function
Referencee Resolution Preset screen size
Screen Match Mode Zoom mode
Match Aspect ratio

In this zoom mode, the UI position is adjusted according to the screen resolution and set aspect ratio. Usually, screen UI adaptation needs to be adjusted to this zoom mode.

First, the official algorithm:

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; }}Copy the code

Screen Match Mode:

Expand: Expand the Canvas Size by width or height

According to the official code, the calculation method is as follows:

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

It means to calculate the length and width respectively, that is, the ratio of Screen Size in Reference Resolution.

For example, the Reference Resolution is 1280 X 720 and the Screen Size is 800 X 600

ScaleFactor Width: 800/1280=0.625 ScaleFactor Height: 600/720=0.83333

Apply the ScaleFactor formula:

Canvas Size = Screen Size/Scale Factor Canvas Width: 800/0.625 = 1280 Canvas Height: 600/0.625 = 960

Canvas Size 1280*960, height changed from 720 to 960, maximum zoom (show all elements)

Shrink: to Shrink the Canvas Size wide or high

According to the official code, the calculation method is as follows:

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

It means to calculate the length and width respectively, that is, the ratio of Screen Size in Reference Resolution.

For example, the Reference Resolution is 1280 X 720 and the Screen Size is 800 X 600

ScaleFactor Width: 800/1280=0.625 ScaleFactor Height: 600/720=0.83333

Apply the ScaleFactor formula:

Canvas Size = Screen Size/Scale Factor Canvas Width: 800/0.625 = 960 Canvas Height: 600/0.83333 = 720

The Canvas Size is 960*720, and the width has changed from 1280 to 960, reducing to the maximum extent

Match Width or Height: Mix scaling based on Width or Height

According to the official code, the calculation method is as follows:

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);
Copy the code

Take logarithms of ScaleFactor Width and Height, and then mix them evenly. Why not use March to mix Width and Height? Let’s compare

Assume that Reference Resolution is 400 X 300 and Screen Size is 200 X 600

The Reference Resolution Width is twice the Screen Size Width

The Reference Resolution Height is 0.5 times the Screen Size

It will look like this:When March is 0.5, ScaleFactor should be 1 (flattened) :

ScaleFactor Width: 200/400 = 0.5

ScaleFactor Height: 600/300 = 2

General mix:

ScaleFactor = March * ScaleFactor Width + March * ScaleFactorHeight

ScaleFactor = 0.5 * 0.5 + 0.5 * 2 = 1.25

Logarithmic mixing:

LogWidth: log2(0.5) = -1

LogHeight: log2(2) = 1 logWeightedAverage: 0 ScaleFactor: 20 = 1

ScaleFactor is usually blended at 1.25 and logarithmic blended at 1, and it is clear that logarithmic mixing can be used to correct the size more perfectly.

2-3. Constant Physical Size

Attribute Description:

1, Physical Unit

The unit type unit Compare that to an inch
Centimeters Cm (cm, cm) 2.54
Millimeters Mm (mm, mm) 25.4
Inches inches 1
Points point 72
Picas pickup 6

Fallback Screen DPI: standby DPI. If no device DPI can be found, use this value

Official code:

float currentDpi = Screen.dpi;
float dpi = (currentDpi == 0 ? m_FallbackScreenDPI : currentDpi);
float targetDPI = 1;
switch (m_PhysicalUnit)
{
    case Unit.Centimeters: targetDPI = 2.54 f; break;
    case Unit.Millimeters: targetDPI = 25.4 f; 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);
Copy the code

conclusion

■ ScaleFactor is the ratio of “current hardware DPI” to “target unit”

■ ReferencePixelsPerUnit should calculate the new value with the current Dpi, and then pass it into Canvas to calculate the size, the formula is as follows:

New Reference Pixels Per Unit = Reference Pixels Per Unit * Physical Unit/Default Sprite DPI

UI size = Original Pixels/(Pixels Per Unit/new Reference Pixels Per Unit)