Tasharen Entertainment Forum

Support => NGUI 3 Support => Topic started by: davidgutierrezpalma on June 23, 2013, 06:22:47 AM

Title: Feature Request: Scaling GUI using "Real World" units.
Post by: davidgutierrezpalma on June 23, 2013, 06:22:47 AM
At this moment, we can use two different scaling options at the UIRoot:

Let's suppose we have a lot of text that has been optimized for a normal medium-res screen:
For that reason, I think it could be useful to have a Fixed Real World Size option for the UIRoot component that would make the text retain the same size (in "Real World" units) in every screen, regardless of their size and resolution. We could implement such feature if we take into account the Screen.dpi (in addition to the Screen.height) when we calculate the scale of the different elements of the GUI:

  1. public float dpi{
  2.         get{
  3.                 float dpi = Screen.dpi;
  4.                        
  5.                 // If Unity is unable to detect the DPI of the screen,
  6.                 // 1) Use 160 DPI as default value for mobile devices
  7.                 // 2) Use 72 DPI as default value for other Mac.
  8.                 // 3) Use 96 DPI as default value for other platforms.
  9.                 if (dpi < Mathf.Epsilon){
  10.                         if      (
  11.                                         Application.platform == RuntimePlatform.Android ||
  12.                                         Application.platform == RuntimePlatform.IPhonePlayer
  13.                                 ){
  14.                                 return 160f;
  15.                         }else if(
  16.                                         Application.platform == RuntimePlatform.OSXEditor ||
  17.                                         Application.platform == RuntimePlatform.OSXPlayer ||
  18.                                         Application.platform == RuntimePlatform.OSXWebPlayer ||
  19.                                         Application.platform == RuntimePlatform.OSXDashboardPlayer
  20.                                 ){
  21.                                 return 72f;
  22.                         }else{
  23.                                 return 96f;
  24.                         }
  25.                 }
  26.                 return dpi;
  27.         }
  28. }
  29.  
  30. public float GetRealWorldSize(int pixels){
  31.         // The dpi of the devices are usually measured in diagonal,
  32.         // so we need to calculate the "diagonal number of pixels"
  33.         // per inch to calculate the correct pixel size (in inches).
  34.         float pixelSize = Mathf.Sqrt(2f) / this.dpi;
  35.        
  36.         return (float)(pixels) * this.pixelSize;
  37. }
  38.  

We could use the code above to calculate the "Real World" size of a GUI element and we could use that information to modify the scale such element so its "Real World" size is the same in every screen. If we had the text from my previous example, it would fill a large portion of the screen on the small high-res screen and it would fill a small percentage of the screen on the large low-res screen, but the text would be readable on both screens because the "Real World" size of the text would be exactly the same on both cases.

I think I could implement this feature without many problems, but it would be a pain in the ass to re-apply my changes with every new release (and we know that NGUI is updated very often ;D). That is the reason why I'm making a feature request instead of implementing this feature myself... ;)

//----------//

Edited for including a different "default dpi" for Windows and Mac platforms.
Title: Re: Feature Request: Scaling GUI using "Real World" units.
Post by: ArenMook on June 23, 2013, 10:20:55 AM
And how would this DPI be used? Not sure what the '72' or '162' is going to be used for afterwards.
Title: Re: Feature Request: Scaling GUI using "Real World" units.
Post by: davidgutierrezpalma on June 23, 2013, 05:18:16 PM
According to the Unity documentation, Screen.dpi will return 0 if Unity is unable to determine the dpi of the screen, so I was trying to provide a reasonable "default value" for each platform in case Unity couldn't retrieve the dpi of the screen. Why have I chosen those "magic numbers"?

I have chosen 160 dpi as the default value for mobile platforms because, according to the Android SDK documentation, "a 160 dpi screen is the baseline density assumed by the system for a 'medium' density screen", so I thought it was a good idea to use that value.

I chose 72 dpi as the default value for non-mobile platforms because I read in the Wikipedia that 72 dpi has been the default dpi value for Mac OS since 1980, but I didn't realize that the default dpi value for Windows is 96 dpi, so the code above should be modified to provide a different "default dpi values" Windows and Mac...
http://blogs.msdn.com/b/fontblog/archive/2005/11/08/490490.aspx (http://blogs.msdn.com/b/fontblog/archive/2005/11/08/490490.aspx)

About Linux, it seems that different distros use different "default dpi values", but I have seen a few distros that uses 96 dpi as default, so I think we can use that value safely.

//----------------------------------------//

I think the best place to invoke the GetRealWorldSize() method would be on the UIRoot script, something like this:

  1. public int activeHeight
  2. {
  3.         get
  4.         {
  5.                 int height = Mathf.Max(2, Screen.height);
  6.                 if (scalingStyle == Scaling.FixedSize) return manualHeight;
  7.                
  8.                 //---------------------------------------------------------------
  9.                 // MY CHANGE (start)
  10.                 //---------------------------------------------------------------
  11.                 if (scalingStyle == Scaling.FixedRealWorldSize) return GetRealWorldSize(manualHeight);
  12.                 //---------------------------------------------------------------
  13.                 // MY CHANGE (end)
  14.                 //---------------------------------------------------------------
  15.  
  16. #if UNITY_IPHONE || UNITY_ANDROID
  17.                 if (scalingStyle == Scaling.FixedSizeOnMobiles)
  18.                         return manualHeight;
  19. #endif
  20.                 if (height < minimumHeight) return minimumHeight;
  21.                 if (height > maximumHeight) return maximumHeight;
  22.                 return height;
  23.         }
  24. }
  25.  

Maybe it would be interesting having a "Scaling.FixedRealWorldSizeOnMobile" value too...
Title: Re: Feature Request: Scaling GUI using "Real World" units.
Post by: DragonSpawn on March 02, 2014, 01:58:59 PM
I can't really stress how much this is missed.
David mentions mostly text, but the same applies to textures.
The current options available in NGUI doesn't quite suit GUI development on mobiles.
Pixel perfect doesn't fit all sizes, and Fixed uses scaling which isn't really what you want to use when spanning a wide range of resolutions.
https://developer.android.com/guide/practices/screens_support.html
Using density-independent pixels should be a third alternative.
That way you are guaranteed to always have the GUI look the same on devices on all shapes and resolutions.
I had this for the GUI I was working on, but it was taking too much of my time away from gameplay coding so I wanted to check out some other plugins, but it doesn't really seem that anyone has given it much thought.

Having this, and with the user creating atlases for different densities, you would have pixel perfect guis on all mobile devices, without need for scaling.
Title: Re: Feature Request: Scaling GUI using "Real World" units.
Post by: ArenMook on March 03, 2014, 12:29:58 AM
To add support for a DPI-based UIRoot, all you need to do is use a script like this:
  1. using UnityEngine;
  2.  
  3. public class SetHeightBasedOnDPI : MonoBehaviour
  4. {
  5.     void Start()
  6.     {
  7.         UIRoot root = GetComponent<UIRoot>();
  8.         float dpi = Screen.dpi;
  9.         if (dpi == 0f)
  10.         {
  11.             if (platform == RuntimePlatform.Android ||
  12.                 platform == RuntimePlatform.IPhonePlayer ||
  13.                 platform == RuntimePlatform.BB10Player ||
  14.                 platform == RuntimePlatform.WP8Player) dpi = 160f;
  15.             else dpi = 96f;
  16.         }
  17.         root.manualHeight = Mathf.RoundToInt(Screen.height * 96f / dpi);
  18.     }
  19. }
Title: Re: Feature Request: Scaling GUI using "Real World" units.
Post by: DragonSpawn on March 03, 2014, 02:37:56 PM
Thanks.
Following that example I got it working mostly. The problem with that piece of code is that it resizes based on whether it is landscape or portrait mode.
I however modified it with the example here http://www.tasharen.com/forum/index.php?topic=2397.0 and got it working perfectly.

Should definitely be it's own scaling style in my opinion.

Thanks for the help.
Title: Re: Feature Request: Scaling GUI using "Real World" units.
Post by: Nicki on March 03, 2014, 04:08:49 PM
Personally, I'd like to be able to set some sort of dpi for touch input as well, so it's not stuck on a certain pixel amount for drag and click distinctions. For right now, I tend to use an external script that calculates the dpi (either based on screen.dpi or custom stuff) and sets the pixel values at run time.
Title: Re: Feature Request: Scaling GUI using "Real World" units.
Post by: ArenMook on March 03, 2014, 10:42:16 PM
You can currently set both the DPI adjustment and the "shrink UI in portrait mode" setting on the UIRoot in the Pro version.