EDIT: TL;DR These are my original, personal notes from trying to learn about the canvas scaler which are in depth, but sometimes nearly unreadable. I added a post further down that does a better job of explaining my general thoughts on how you should configure the scaler.
I’ve been doing a lot of work with UI lately and for my own sake I spent a lot of time trying to get my head fully around the CanvasScaler, specifically the Screen Match Mode and Reference Resolution. It’s important to fully understand this component and its properties at the outset of your UI design. Otherwise you may not fully grok how to specify the size of your UI elements in their rect transform without just anchoring their corners. You may not have a complete visualization of how your game or app will look on many different screen sizes and orientations. If you have to come back later and change the values of your main CanvasScaler it can really effect your whole UI design. I’ve spent a lot of time digging around in the UI source code (thanks to Unity for exposing this!) to understand this and many other aspects of Unity UI. Hopefully these notes I made for myself can be useful to others trying to understand what to think of CanvasScaler.
Canvas
RenderMode: Screen Space - Overlay vs. Camera
Camera mode applies an aditional scalar to the scale factor to shrink the UI to the size of the frustum.
Canvas Scaler
One important thing to visualize is that while the canvas scaler changes the scale of the canvas it simultaneously changes the size of the canvas. The end result is an effect similar to the way a dolly zoom works. The size of the canvas in world space does not change because of the canvas scaler. It always matches the pixel resolution of the screen. The canvas is sized and scaled to fit that space based on the chosen canvas scaler properties.
UI Scale Mode: (Scale With Screen Size) - You almost never want to have constant pixel size or a constant physical size.
Reference Resolution: (1024x768) - The basis on which our other numbers for sizing UI are framed. If the reference resolution is 1920 x 1080 then sizing an image’s rect transform to 512 x 512 means 512 pixels in the context of a 1920 x 1080 screen, then scale accordingly based on screen match mode and the actual resolution of the current device. The common denominator for aspect ratio on all platforms is 4:3 / 3:4. There are some desktop monitors that are 5:4, but it’s 1/16 more narrow than 4:3 and not very common. We’ll get to aspect ratios and orientations in the next section, but 1024x768 is by far the most common pixel resolution for 4:3 ratios.
Screen Match Mode: The scale factor for the entire canvas will either be (screenSize.x / m_ReferenceResolution.x) or (screenSize.y / m_ReferenceResolution.y) with match mode expand or shrink and somewhere between those two values with match width or height. (The screenSize variable accounts for the actual size of the viewport rectangle for that camera display on screen if the camera display does not occupy the full screen resolution.) Match width or height is useful for forcing the exclusive use of one of those calculations because the expand and shrink match modes will automatically select between them depending on the screen resolution. The expand match mode chooses the smallest value from those two calculations to use as a scalar and shrink chooses the largest. The size of the canvas will be the pixel resolution of the screen divided by the chosen scale factor, but it wall have that scale factor applied so in world space in the scene view it will be the size of the screen resolution. This is the dolly zoom like effect mentioned before. So if the screen resolution is not the same aspect ratio as the reference resolution, one of the canvas dimensions will match the reference resolution, and the other will stretch by a number of pixels to meet the screen aspect ratio. Which axis is chosen to match the reference resolution (horizontal or vertical) depends not only on the screen match mode, but the relationship between the screen resolution and reference resolution. For expand, the axis that needs to be scaled down the most, or scaled up the least will match the reference resolution. For shrink, the axis that needs to be scaled down the least, or scaled up the most will match the reference resolution. When considering landscape aspect ratios, the only reason a anyone would prefer a wide screen monitor is is if the height of elements on screen stay the same as they would on say a 4:3 screen. If so, you simply have more space horizontally on the wider the screen. If the relative width of elements on screen remain the same, then a wider screen effectively cuts out more and more vertical space as it gets wider. Therefore, on landscape it likely makes the most sense to only respect the height of the screen when determining the scale factor. However, the opposite is true for portrait orientation. This means that for landscape you should design for the most narrow aspect ratio you expect to support, like 4:3. Then, have the elements retain their relative height on screen while spacing UI elements out on wider aspect ratios. Designing for the widest supported aspect ratio would mean that elements would come together and overlap on more narrow screens. Similarly for portrait orientation (phone & tablet), you should design on the shortest supported aspect ratio. If your application supports both landscape and portrait orientation, you may need to create a different canvas for each orientation simply because it makes more sense to have a completely different UI layout for portrait than you do for landscape. So just set the canvas scaler settings appropriately for each (match width or height and set height for landscape and width for portrat). If the aspect ratio of the screen matches the aspect ratio of the reference resolution, then the screen match mode setting does not matter. The calculated scale factor value will be the same no matter the match mode setting. It’s only when the aspect ratio is different from the original design that the match mode becomes important. Also, this may not even change the size of elements in your UI if you are using anchors for everything rather than pixel size values. Remember, the pixel size values are in the context of your reference resolution, not the actual screen size!
ScreenMatchMode.Expand: Expand always scales the rectangle of the reference resolution to fit completely inside of the actual screen resolution. That’s not to say that your actual UI will be formatted that way, but the parent scale factor for all elements in the canvas will be calculated according to that concept. So if the screen aspect ratio is more narrow than the reference, it will choose a scale factor based on width and make sure all of the scaled elements fit the width of the screen properly, while creating additonal empty space vertically between elements. If the screen is wider than the reference it will scale based on height and fit every element’s height to the proper ratio on screen, providing extra horizontal space between elements. This is a good all around choice for handling both landscape and portrait with the same canvas, but should probably be designed with and use a reference resolution that most resembles a square like 4:3 1024x768, or literally using a square reference resolution.
scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
ScreenMatchMode.Shrink: Shrink makes the screen fit over the smallest part of the reference aspect ratio, so some part of that reference rectangle will always hang outside one axis of the screen rectangle. That’s not to say that your actual UI will appear that way, but the parent scale factor for all elements in the canvas will be calculate according to that concept. This almost never seems like what you would want unless you were desiging for portrait and had your reference set to 9:16, but then you could just match by width and that would probably be better. I’m imagining a full screen mobile app where items in a list get consumed into a scrollable rect on shorter screens. So you’ve designed the items in the scroll rect to be the correct relative height on the tallest screen, and the relative width of elements is matched. Therefore as screens get smaller the screen “shrinks” the vertical space available over the design in reference space. Effectively on a tall screen you can see more elements in the list. I don’t see as much use for this concept horizontally in landscape.
scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
ScreenMatchMode.MatchWidthOrHeight:
// 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);
I prefer to set this all the way to height for landscape designs and width for portrat designs because it has the most predictable results. Let’s say you have a 1024x768(4:3) reference resolution for a typical landscape oriented video game and you’re creating the UI. So, instead of using anchors for every bit of your layout, you anchor an image to the corner of the screen and set the size of the rect transform to be 128x128. That size specification is in the context of the reference resolution when using a canvas scaler with scale mode set to scale with screen size. So 128/1024 is 1/8 screen width and 128/768 is 1/6 screen height. Now you can see that those pixel size values on your rect transforms are actually relative screen size values when considered in context of your reference resolution. So if the end user is actually playing on a 1920x1080(16:9) screen and you are set to match height, the scale factor will be 1080/768=1.40625. Every rect in your canvas will be scaled by 1.40625. If that rect is anchored at its corners it doesn’t usually matter. So the size of your image will change to 180 pixels on that screen. So, 180/1080 is 1/6 screen height, just like on our reference design. However, since this is a wider monitor and we designed on our most square supported resolution, 180/1920 is a little more than 1/10 of the screen width. So now there is more horizontal empty space between our UI elements on the wider monitor. Since our element is anchored at its center to the corner of the layout that empty space opens up in the center of the screen for more of the game world.
Reference Pixels Per Unit: Specifically affects images. Similar to reference resolution. If the pixels per unit of the image asset is set to 100 (default) then when divided by reference pixels per unit of 100 (default) the scaling on the image will be 1. This only matters when clicking the “Set Native Size” button and in calculations for tiled and sliced images.