I know you guys have only recently changed the way canvas scaling works but the documentation on how to use Constant Physical Size is really sparse so I was hoping to get some answers here.
I have a HUD for my game that has buttons that ideally I would like to stay the same physical size so no matter the device the buttons are still pressable. In the editor I have designed it to the size I like:
Then I have attached a CanvasScaler component:
All is well and good, but when I run it on my iPhone 5, I get:
Im thinking the problem is that I have designed my HUD using the 96 DPI in the CanvasScaler. The problem is there doesnt seem to be any way to simulate higher DPIs when designing so instead I set the Fallback Screen DPI to the DPI of my iPhone (324).
We now in the editor it looks like it does on the phone:
Okay, so now if I scale my HUD down to that size bad things start happening:
Note the shadow is still set to 1 but looks like it is much greater than 1. The same goes for things like the spacing on Horizontal and Vertical Layout Containers.
So basically im not sure if the way i’m doing things is right, can some Unity staff please give me some guidance on the correct way to develop Constant Physical Size UI’s.
Thats not what im saying. I am saying, how do we use the Constant Physical Size when developing for multiple devices.
I am currently using several different canvas’ for different scaling scenarios. Popups for example must always fit on the screen so they have a ratio based scaling. These HUD items must always be pressable and thus they should be scaled with physical size.
What im looking for is some guidance on how to use Constant Physical Size in the editor.
What Mikeysee is looking for should be quite straightforward, but I’ve also come up short trying to figure it out. Really would be awesome if unity could simulate DPI in the game window, because right now it’s incredibly difficult trying to figure out resolution scaling issues.
I solved the problem by creating my own scaling component. Essentially it’s just scaling the canvas based on some math. Physical size (mm) = pixels / dpi * 25.4
This should give you the clue that to keep physical size constant, you need to increase the number of pixels proportionally to an increase of dpi. So if your dpi is 2x ( eg 320 dpi vs 160 dpi) then your canvas scale factor needs to be set to 2. (Note you will need to swap in a high res sprite if you want to take full advantage of the higher pixel density).
What gets really confusing is the fallback dpi settings on the canvas scaler. Because the editor doesn’t actually have any concept of dpi, it just makes things in the editor weird really quick.
Just drop that on your canvas, instead of CanvasScaler, and it should do the job.
You should just be able to just use DP sizes, and it will scale them appropriately. Also, I’ve got it correcting the scaling in the editor, so it should behave the way you expect.
If you want other versions (e.g. Standalone) to more closely match the element sizes you see in the editor for mobile, just fiddle with the preprocessor directives at line 100.
If you see any bugs or other problems, let me know!
I pasted your script in my project but I don’t really understand how it works.
Although the physical size seems to be conserved, the scaling isn’t like any of the original CanvasScaler’s modes.
My question is: which unit do you use? It doesn’t seem to be any of the original ones (cm, mm, inches, etc.).
The reason my canvas scaler is called “DpCanvasScaler” is because it uses DPs, which is short for “device independent pixels.” You can learn more about them here:
I am using the Android formula for calculating DPs in my canvas scaler. This should make it easy for people who are trying to make an app that satisfies Google’s Material Design style guide (which uses DP for all units), and it should provide consistent element sizing on most mobile devices.
Thanks for the explanation. This method seems to be the best way to have a constant physical UI size on mobile, but does it work as well on standalone PC/Mac or Web?
That’s a great question! It depends entirely on whether the platform correctly reports its Screen.dpi in Unity. The last time I checked, it does pretty well on mobile devices, but it’s not so great on desktop/laptop machines. I provide a Fallback Screen Dpi value that you can use to provide a fallback DPI on devices that return a 0 from Screen.dpi.
I’ve tested it on several Laptops and it didn’t really work. I measured 20 to 25% difference from what I had on my desktop screen (it’s smaller than it should be), which is more than what I get with Unity’s CanvasScaler. I’m guessing Screen.dpi just doesn’t return the right number, although I don’t know why.
It works perfectly on Android tablets though (but I’ve only tried on 2).
Yeah, I wish I could give you a better solution for computers, but if Unity doesn’t provide me with a DPI value, I have no way to guess what their physical pixel density is.
I’ve turned off my canvas scaler script and have your dp canvas scaler script on my canvas instead. Everything looks relatively the same in the editor, but when I send my apk to my android devices, the ui elements are sort of smaller and a little blurry around the edges. Kind of like if I set default sprite dpi to a smaller number like 50 or if I set reference pixels per unity higher, like 200. I’ve tested this on both a phone and tablet with the same results. Howeve, I have confirmed that ui elements are the same size on both devices, which is great.
I have everything set to default reference pixels per unit set at 100, fallback screen dpi and default sprite dpi set to 96, and dynamic pixels per unit set to 1. I don’t really fully understand what all these settings are or what I can do to fix this issue. Any help is greatly appreciated.
Edit: I should mention that I’m using the UGUI’s default sprits.
Your solution worked a treat on my Android build. The Unity one seems to be a total bust, don’t understand how they could get that so wrong, it’s not rocket science. Especially compared with everything they get right.
I assume you reported it to them as an issue?