This article has been written using Unity 5.2.3 as a reference, many things could have been changed inthe current version.
When it’s time to publish a standalonegame made in Unity 3D, one of the things to dois ensuring that thegame and its UI work fine on different display resolutions. Shipping your game with the Unity resolution dialog window turned on (Edit menu > Project settings > Player | Resolution and Presentation : Display Resolution Dialog) is an easy way to let your users choose a windowed or fullscreen resolution for thegame. But if you want to let themchange the resolution from an in-game menu or if you haveto enforce a specific aspect ratio, than you need to understand how Unity screen resolution management works, its connection to the PlayerPrefs and how to tune cameras to target a specific aspect ratio. I’ve come up with a basic screen resolution manager for Unity, tested on OS X and Windows. Here is the project on github:UnityScreenResolutionManager[a].In this article I will explain in detail how it works.
Contents
- Full Screen Resolution
- Full Screen Aspect Ratio
- PlayerPrefs
- Screen Resolution Manager
- Linux
- Notes
Full Screen Resolution
Edit > Project settings > Player
Let’s see how the fullscreen resolution works in Unity. A set of parameters can be edited fromEdit menu > Project settings > Player | Resolution and Presentation (see image). The first thing to look is the fullscreen mode. You can choose between fullscreen window and exclusive mode on Windows. Inexclusive mode your game will take control of the entire display. With this modeit is not possible to change the screen resolution at runtime, thus we have to avoid this mode and choosethe fullscreen window modeinstead. In fullscreen window mode, your game will run inside a maximizedborderless window. This window will live in the current screen, thus inheriting the same resolution of the display where it has been created, no changes are made to the current display resolution when the application starts and when it is running. Now let’s see what this implies when you set a fullscreen resolution using the editor player settings:
- When you checkDefault is native resolution in player settings, native means the current display resolution, not the actualnative resolution of you computer monitorwhich can be larger than the current screen resolution.
- When you select a Default Screen Width/Heightin player settingsgreater than the current display resolution, Unity will use the current display resolution, even if it is smaller than your monitor native resolution (no display resolution changes are possible in fullscreen window mode). Let’s say that you have a full hd monitor and you set a 1280×720 resolution using the OS settings; when you launch a Unity application set with a default full hd resolution, the application will run inside a maximized window on your 1280×720 screen, no screen resolution change will occur.
- When you select a Default Screen Width/Height smallerthan the current display resolution, Unity will use this smallersize to render the current fram in memoryand then upscale it to fit the full-resolution window. You will see larger pixels, as when you set a lower display resolution, but this upscale is done inengine, not by changing the actual displayresolution.
As we have seen above, when dealing with screen resolutions in Unity, it is important to distinguish between the game viewport resolution and the underlying display resolution. Unity documentation talks about a general screen resolution which can be unclear if you don’t know much about the way Unity manages screen resolutions. In this article I will always specify if I’m talking about thedisplay resolution or thegame viewport resolution. You can control the screen resolution at runtime usingthe Unity engine Screen class:
- Screen.fullscreen Toggles fullscreen/windowed mode.
- Screen.width, Screen.heightThe current width/height of the game viewport in pixel.
- Screen.resolutionsAll fullscreen resolutions supported by the monitor. The resolutions included in this array are filtered by the Supported aspect ratiossetting you can find in the player settings panel. If your monitor is set to a resolution smallerthan native, Screen.resolutions will contain the native resolution but that resolution will not be actually available for your game; in fact, as seen above, it is impossible to increase the display resolution at runtime.
- Screen.currentResolutionThis property has a different behaviour onWindows, OS X and Linux when you are in fullscreen mode.
- On Windows it contains the current game viewport resolution (the one that you set with Screen.SetResolution), but in certain cases it can be different from (Screen.width, Screen.Height).
- On OS X it contains the current display resolution (set by the OS).
- When in windowed mode, Screen.currentResolution gives you the current display resolution both on Windows and OS X.
- On Linux (tested on Ubuntu 14.10) this parameter is pretty useless on a multiplemonitor configuration cause it will always returnthe resolution of the screen area covered by all the monitors both in fullscreen and in windowed mode.
- Screen.SetResolution(int width, int height, bool fullscreen) Sets the game viewport widthand height in pixels and switches between fullscreen and windowed mode. You are free to set a resolution not includedin the array Screen.resolutions; you can choose any size smaller or equal to the current display resolution (not greater, see above). When you choose a resolution with an aspect ratio different from the current display aspect ratio, Windows can addletterbox or pillarbox black bands (only when you set the resolution in player settings, not at runtime), OSX will stretch the viewport on the entire display with no black bands thus changing the pixel aspect ratio and generatinga distorted image. We’ll see below the best way to deal with aspect ratio in Unity.
Full Screen Aspect Ratio
As seen above, when you set a fullscreen resolution with an aspect ratio different from the current display aspect, Unity can insertblack bands around the screen to keepthe pixel aspect ratio. Unfortunately you cannot count on this behaviour for two reasons: it only works on Windows (OS X stretches the screen), and the black bands on Windows are not refreshing (Steam popups will leave a trail over these bands). The correct way to deal with aspect ratios and insert black letterbox/pillarbox bands is working with camera settings. There is a nice script on the Internet – AspectUtility.cs[b] – which is very simple to use: just add it to every camera in your scene. If you are using Unity legacy UI or NGUI, make sure to use screen space camerarender mode and add the AspectUtility.cs to the UI camera. This script will set the camera rect to create some pillarbox/letterbox bands according to the proportionbetween the current and desired aspect ratio. It will also add a background camera to fill these black bands and guarantee screen refreshing for these areas. It also provides some utility methods to remap screen sizes and mouse positions, useful when you are working with the game UI. Using this script, the way you enforce screen aspect ratio in your game is:
- Set a fullscreen resolution with the same aspect ratio of your display. In this way you are sure that Unity won’t stretchthe screen thus preventing image distorsion.
- Add the AspectUtility.cs script to each camera in your scene, using screen space camera rendering for UI.
I did some changes to the original AspectUtility.cs script in order to intercept screen resolution changes and update the camera rect accordingly.
PlayerPrefs
Every time you change the screen resolution, Unity saves the width, height and fullscreen/windowed mode in the application PlayerPrefs storage.Unity applications read these PlayerPrefs at startup and use these values to set the screen resolution for your game. This can lead to some strange behaviours when testing your applications:
- When you make new builds of your application with different screen settings, the application will keep reading the old PlayerPrefs at startupignoring the new player settings. You have to delete the current PlayerPrefs in order to test the behaviour of an application first run.
- If you run your application on a display with a resolution smaller than the one specified in player settings, the screen will adapt to this smaller display and the new screen size will be stored in PlayerPrefs. Now, if you close the application and then you launch it again on a monitor with higher resolution (a common case is when you have a multi monitor setup with different screen sizes), your application will keep the previoussmallerresolution even if now yourmonitor could support the native game resolution.
On Windows PlayerPrefs are stored in the registry under HKCU\Software\[company name]\[product name] key, where company and product names are the names set up in Project Settings. Just delete this registry entry to make sure that your application does not read old screen settings at startup. On Mac OS X PlayerPrefs are stored in ~/Library/Preferences folder, in a file named unity.[company name].[product name].plist, where company and product names are the names set up in Project Settings. The same .plist file is used for both Projects run in the Editor and standalone players. Cleaning up PlayerPrefs on OS X is a bit harder than on Windows cause the application itself and the per user process “cfprefsd
” both cache the preferences[c]. The easiest way I found to cleanup PlayerPres on OS X is to callPlayerPrefs.DeleteAll() inside an ad hoc cleanup scene in my Unity project.
Screen Resolution Manager
Now let’s see a bit of code of myscreen resolution manager for Unity. Here is the project on github:UnityScreenResolutionManager. A basicscreen resolutionmanager class should:
- Compute a set of available fullscreen/windowed resolutions for your display (according to an optional desired aspect ratio) in order to lette userschoose them in a menu.
- Enforce a preferred aspect ratio at runtime.
- Manage fullscreen to windowed transitions.
To compute the available resolutions, the manager needs to know the current display resolution. On Mac OS X it’s easy, just read the Screen.currentResolution property. When running in fullscreen mode on Windows, as seen above, this property gives you the current game viewportresolution, which can be different from that of the display. The solution I used to get the current display resolution when you start fullscreen is switching to windowed mode at startup, read the Screen.currentResolution property and then switch back to fullscreen. Here is the code:
1 2 3 4 5 6 7 8 9 10 11 12 See Also Reinstalling and patching your Quicken Subscription version after your membership has expired (Quicken for Mac)Quicken Subscription Membership FAQsReinstalling and patching your Quicken Subscription version after your membership has expiredQuicken Review (2023): Features, Pricing & More13 14 15 16 17 18 19 20 21 22 23 24 | if (Application.platform == RuntimePlatform.OSXPlayer) { NativeResolution = Screen.currentResolution; } else { // If fullscreen on Windows go windowed to read the current display resolution if (Screen.fullScreen) { Resolution r = Screen.currentResolution; Screen.fullScreen = false; // goto windowed // Wait a couple of frames to make sure that // Screen.currentResolution updates its value yield return null; yield return null; NativeResolution = Screen.currentResolution; Screen.SetResolution (r.width, r.height, true); yield return null; } else { NativeResolution = Screen.currentResolution; } } |
The above code which is executedinside a coroutine, as you can guess if you notice theyield keywords. This becauseI need to wait one or two frames when changing resolution (the resolution is actually changed in the next frame after calling Screen.setResolution). After obtainingthe current display resolution, it is possible tocompute a set of resolutions, based on a list of predefined hardcoded widths.
1 2 | // List of horizontal resolutions to include int[] resolutions = new int[] { 600, 800, 1024, 1280, 1400, 1600, 1920 }; |
I’m creating two lists, both for windowed and fullscreen resolutions. Here are the steps:
- Add to the fullscreen list all the resolutions smaller than 80% of the current resolution.
- Add to the fullscreen list half the value of the current screen resolution (only if greater than the smallest hardcoded resolution).
- Add to the fullscreen list the current display resolution.
- Add all resolutions smaller than 80% of the current resolution to the windowed list.
On Mac OS X (at least on Yosemite where I did the tests) there is a problem when you try to switch from fullscreen mode to windowed mode with a specific window size. The application, in fact, will pass from fullscreen to windowed with an animated transition of a couple of seconds.After this transition you have to call SetResolution again to ensure that the window is resized correctly. I wrote a piece of code for OS X which waits for this transition to end, here it is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | private IEnumerator SetResolutionAfterResize(Vector2 r) { int maxTime = 5; // Max wait for the end of the resize transition float time = Time.time; // Skipping a couple of frames during which the screen size will change yield return null; yield return null; int lastW = Screen.width; int lastH = Screen.height; // Waiting for another screen size change at the end of the transition animation while (Time.time - time < maxTime) { if(lastW != Screen.width || lastH != Screen.height) { Screen.SetResolution ((int)r.x, (int)r.y, Screen.fullScreen); yield break; } yield return null; } } |
Here is a screenshot of the UnityScreenResolutionManager:
A simple test scene and UI for the screen resolution manager.
Linux
The screen resolution manager has some problems on Linux, at least on an Ubuntu 14.10 where I did the tests. The aspect ratio enforcer works fine but the screen resolution module has some issues. As seen above, on a multi monitor setup it will get a wrong current display resolution and it will set a very large resolution corresponding to the area covered by all the screens. On a single monitor setup it works fine; the only issue is when you switch for the first time from fullscreen to windowed mode. You will get a maximized windowwhich cannot be resized by script until you manually unmaximize it. Given these issues and the fact that on Linux it is impossible to test the infinite number of different configurations, I prefer to showthe Unity screen resolution dialog at startup on my Linux builds.