Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Switch Monitor at runtime

Discussion in 'Scripting' started by InvincibleCat, Oct 23, 2017.

  1. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    I pinged our documentation team about this. They said they'll look at it.
     
  2. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    221
    Only wish we could have it in 2020 LTS :(. Maybe I can postpone release...
     
    Marcos-Elias likes this.
  3. dvoronin

    dvoronin

    Joined:
    Oct 19, 2016
    Posts:
    16
    Great news!

    I have a question re VSync with 2 monitors setup.

    I have 2 monitors (2 displays) with different refresh rate (60Hz & 75Hz).
    We will be able to change displays in runtime. What gonna happen with VSync? Will it "pick" the current monitor refresh rate?

    At the moment for some reason game picks the lowest monitor refresh rate on both monitors (I am using "UnitySelectMonitor").
     
    chadfranklin47 likes this.
  4. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    Exclusive fullscreen mode should default to native refresh rate. Windowed/fullscreen window modes ALWAYS use the refresh rate that your Windows is configured to. In other words, it will act the same as if you dragged the window manually.

    That said, different windows versions handle mixed refresh rates differently so the result will also depend on your exact Windows version.
     
  5. dvoronin

    dvoronin

    Joined:
    Oct 19, 2016
    Posts:
    16
    I use exclusive fullscreen.

    So my scenario is.
    Fullscreen mode: exclusive fullscreen.
    2 monitors connected: 60Hz and 75Hz (primary).

    I run game on 75Hz monitor with VSync on - game runs on 60 FPS.
    I switch to 60Hz monitor - game runs on 60 FPS.

    Game never runs on 75FPS on 75Hz monitor with 2 monitor setup (exclusive fullscreen).

    If I disconnect 60Hz monitor - everything is fine, game runs on 75 FPS.
     
  6. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    Is the 75 Hz monitor set as primary monitor? Do other games run at 75 FPS on it? What refresh rate do you pass to Screen.SetResolution when switching to exclusive fullscreen, and what does Screen.currentResolution return in exclusive fullscreen mode on the 75 Hz monitor?
     
  7. dvoronin

    dvoronin

    Joined:
    Oct 19, 2016
    Posts:
    16
    @Tautvydas-Zilys
    1. Yes 75Hz monitor is primary
    2. It depends on the game, I did some tests
      • Hollow Knight - fail, 60 FPS
      • Dead Cells - fail, 60 FPS
      • No Mans Sky - ok, 75 FPS
    3. Hm actually I did not pass any refresh rate to Screen.SetResolution, should I?
    4. Well it returns 1920 x 1080 @ 60Hz
    So after that I tried passing 75 refresh rate to Screen.SetResolution and it seems to fix the issue.

    What is this preferredRefreshRate?

    Docs say:
    If preferredRefreshRate is 0 (default) Unity will switch to the highest refresh rate supported by the monitor.
    If preferredRefreshRate is not 0 Unity will use it if the monitor supports it, otherwise will choose the highest supported one.

    I tried passing 0, but it results in 60FPS on 75Hz monitor (with 2 monitors connected).
    I pass 75, everything is ok, 75FPS.
    I pass 200, everything is ok, 75FPS.

    So it seems the issue is following: with 2 monitors connected if preferredRefreshRate is 0 (default) Unity won't pick the highest refresh rate supported by the monitor.
     
  8. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    I think this is slightly incorrect. What the code actually does is it picks the refresh rate that's configured in windows settings. Are you sure that your monitor isn't configured to 60 hz in windows settings?
     
    Clonkex likes this.
  9. dvoronin

    dvoronin

    Joined:
    Oct 19, 2016
    Posts:
    16
    Oh, you are right.
    The monitor was configured to 60hz.
    I changed it to 75hz and now refresh rate is picked correctly without passing any preferredRefreshRate to Screen.SetResolution

    Thanks!
     
  10. dvoronin

    dvoronin

    Joined:
    Oct 19, 2016
    Posts:
    16
    @Tautvydas-Zilys could you please explain what is the expected behavior for FullScreenMode.FullScreenWindow in this scenario (with 2 monitors & VSync on)?

    Will it always pick refresh rate from primary monitor?
     
  11. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    FullScreenWindow doesn't change the display mode, and runs in whatever way Windows makes us run.
     
  12. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    Unity 2021.2.0a16 has been out for a week now: has anyone been able to give this new feature a whirl yet?
     
  13. rzubek

    rzubek

    Joined:
    Aug 21, 2014
    Posts:
    72
    Same here :) @Tautvydas-Zilys - is there any chance this might get backported to 2020.4 LTS? :)
     
  14. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    No, this is a new feature and we don't backport new features. That would defeat the purpose of having a stable, non-changing release.

    Has anyone been able to try it out in 2021.2 beta? Would love to hear feedback!
     
    ModLunar likes this.
  15. flyQuixote

    flyQuixote

    Joined:
    Jun 23, 2013
    Posts:
    25

    Hey, I tried out this feature as I was attempting to add this to my project recently. It works fine in the 2021.2 beta to switch monitors but I found a weird bug. If the target monitor has a resolution lower than the currently selected resolution, then it won't switch properly when going to the new monitor. I would assume this is because setting a resolution higher than that which a monitor could provide causes errors.

    As an example, I was projecting the test project onto a large TV screen with a 4k monitor (or something close to that) and my normal screen only had a 1920x1080 monitor. Switching from small to large, no problem. Switching from larger to small however, this lead to an error when it would just switch back to the larger monitor. I'm not sure if this is a problem with the API or with the MoveWindowSample. https://github.com/Unity-Technologies/DesktopSamples/blob/master/MoveWindowSample

    If you're still working on the feature, I could setup some test with old monitors of varying resolutions I have around the house and write down the results if you're interested.

    It also seemed that it only loaded the monitors on launch, I could adjust it to detect when a new monitor has been loaded and attempt to add it as a possible display (or removed for that matter, I haven't tested that yet either). I'm probably going to wait until 2021.2 is fully released to add it to my project but if you're interested I can try the script further and search for the individual use cases later this weekend.
     
  16. Skjalg

    Skjalg

    Joined:
    May 25, 2009
    Posts:
    211
    2021.2 is still in alpha.

    And I sincerely doubt anyone are using alphas for their main projects right now. Could you please look at back porting this to at least 2021.1 ? I know its a new feature, but it seems like it doesn't really affect anything else except adding new properties and methods to the Screen class, so that it wont affect anyone that just upgrades.
     
    Novack and rzubek like this.
  17. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    Which platform did you find this bug on? There was a known issue on macOS but I believe we fixed it in newer alpha. Could you file a bug report and describe your setup so we could reproduce it?
     
  18. flyQuixote

    flyQuixote

    Joined:
    Jun 23, 2013
    Posts:
    25
    Hello, I'm using a Windows laptop with one external monitor and a built in laptop display. The built in laptop display has a maximum resolution of 1920x1080 but the external monitor has a maximum resolution of 3840x2160.

    To re-create the error, I build the sample project (https://github.com/Unity-Technologies/DesktopSamples/blob/master/MoveWindowSample) using unity 2021.2.0a16 then:
    1. Set it to exclusive full screen mode
    2. Set the resolution to 3840x2160,
    3. Attempted to switch the display to the built in monitor instead of the external monitor
    This caused the game to stay on the previous monitor but change the resolution to 1920x1080 instead of switching to the previous monitor.

    I can try more permutations of settings and look further into the example code and file a bug report. Although, I'm not sure how to file a bug report for alpha though as I haven't worked with the unity alpha. I see that there was a beta build released (2021.2.0b1) and I found the same error repeatable with that version.
     
  19. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
  20. VoodooDetective

    VoodooDetective

    Joined:
    Oct 11, 2019
    Posts:
    238
    I've been trying out this new API and have run into several issues:

    1. I believe there is an off-by-one error in the example project where it computes the number of non-duplicate resolutions. I created an "issue" here.
    2. I'm working on an iMac with an external display. The resolutions returned by Screen.resolutions returns all resolutions for all monitors. That's fine, except I can't tell which resolutions are valid for which monitor because all DisplayInfo objects returned from the new API have the same width/height. The width/height of the build-in screen for the iMac which is different from my other external monitor I'm testing with.
    3. When I use the new "Screen.MoveMainWindowTo" method, the screen switching seems to work the first one or two times I use it to switch displays. But after some non-deterministic number of screen swaps, Unity will hide the screen completely. I checked the screen position and it says (0,0). But I can't find the window anywhere. I'm only testing in windowed fullscreen mode. I've tried this out with the test project, but haven't been able to reproduce the issue there. However, I've largely copied the code from that project and the only difference is that I'm using buttons to change display rather than a drop down. I'll attach my code below.
    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. namespace ScreenManagement
    7. {
    8.     /// <summary>
    9.     /// Central conduit through which we deal with screen related things.
    10.     /// NOTE: there's a bug in Unity's display API that causes:
    11.     ///       - All resolutions to be returned regardless of which screen you're on
    12.     ///       - The largest screen resolution to be returned for every screen's DisplayInfo
    13.     /// </summary>
    14.     public class ResolutionManager : MonoBehaviour
    15.     {
    16.         // Static
    17.         private static readonly ResolutionComparer resolutionComparer = new ResolutionComparer();
    18.  
    19.         public static ResolutionManager instance { get; private set; }
    20.  
    21.         // Events
    22.         public static event Action<IResolutionInformation> OnResolutionChange;
    23.         public static event Action<IDisplayInformation> OnDisplayChange;
    24.         public static event Action<FullScreenMode> OnFullscreenChange;
    25.  
    26.         // Properties
    27.         private Coroutine changeDisplayRoutine;
    28.         private Resolution[] emptyResolution = new Resolution[1]; // To keep from gening up a bunch of arrays
    29.         private DisplayInformation displayInfo = new DisplayInformation();
    30.         private ResolutionInformation resolutionInfo = new ResolutionInformation();
    31.  
    32.         void Awake() => instance = this;
    33.  
    34.         #region Fullscreen Methods
    35.         public static FullScreenMode CurrentFullscreenMode() => Screen.fullScreenMode;
    36.  
    37.         public static void SetFullscreen(bool fullscreen)
    38.         {
    39.             if (fullscreen)
    40.             {
    41.                 DisplayInfo display = Screen.mainWindowDisplayInfo;
    42.                 Screen.SetResolution(display.width, display.height, FullScreenMode.FullScreenWindow);
    43.                 OnFullscreenChange(FullScreenMode.FullScreenWindow);
    44.             }
    45.             else
    46.             {
    47.                 Screen.fullScreenMode = FullScreenMode.Windowed;
    48.                 OnFullscreenChange(FullScreenMode.Windowed);
    49.             }
    50.         }
    51.         #endregion
    52.  
    53.         #region Resolution Methods
    54.         public static IResolutionInformation CurrentResolutionInformation()
    55.         {
    56.             instance.RefreshResolutionInformation();
    57.             return instance.resolutionInfo;
    58.         }
    59.  
    60.         public static void IncreaseResolution()
    61.         {
    62.             instance.RefreshResolutionInformation();
    63.             int nextIndex = instance.resolutionInfo.NextResolutionIndex();
    64.             if (nextIndex == instance.resolutionInfo.currentResolutionIndex) return;
    65.             Resolution nextResolution = instance.resolutionInfo.resolutions[nextIndex];
    66.             Screen.SetResolution(nextResolution.width, nextResolution.height, Screen.fullScreenMode);
    67.             instance.resolutionInfo.currentResolutionIndex = nextIndex;
    68.             OnResolutionChange(instance.resolutionInfo);
    69.         }
    70.  
    71.         public static void DecreaseResolution()
    72.         {
    73.             instance.RefreshResolutionInformation();
    74.             int prevIndex = instance.resolutionInfo.PrevResolutionIndex();
    75.             if (prevIndex == instance.resolutionInfo.currentResolutionIndex) return;
    76.             Resolution prevResolution = instance.resolutionInfo.resolutions[prevIndex];
    77.             Screen.SetResolution(prevResolution.width, prevResolution.height, Screen.fullScreenMode);
    78.             instance.resolutionInfo.currentResolutionIndex = prevIndex;
    79.             OnResolutionChange(instance.resolutionInfo);
    80.         }
    81.         #endregion
    82.  
    83.         #region Display Methods
    84.         public static IDisplayInformation CurrentDisplayInformation()
    85.         {
    86.             instance.RefreshDisplays();
    87.             return instance.displayInfo;
    88.         }
    89.  
    90.         public static void NextDisplay()
    91.         {
    92.             if (instance.changeDisplayRoutine != null) return;
    93.             instance.RefreshDisplays();
    94.             int nextIndex = instance.displayInfo.NextDisplayIndex();
    95.             if (nextIndex == instance.displayInfo.currentDisplayIndex) return;
    96.             instance.changeDisplayRoutine = instance.StartCoroutine(instance.MoveToDisplay(nextIndex));
    97.         }
    98.  
    99.         public static void PrevDisplay()
    100.         {
    101.             if (instance.changeDisplayRoutine != null) return;
    102.             instance.RefreshDisplays();
    103.             int prevIndex = instance.displayInfo.PrevDisplayIndex();
    104.             if (prevIndex == instance.displayInfo.currentDisplayIndex) return;
    105.             instance.changeDisplayRoutine = instance.StartCoroutine(instance.MoveToDisplay(prevIndex));
    106.         }
    107.         #endregion
    108.  
    109.         #region Information Refresh Methods
    110.         private void RefreshResolutionInformation()
    111.         {
    112.             // Filter out non-unique resolutions
    113.             Resolution[] resolutions = Screen.resolutions;
    114.             Array.Sort(resolutions, resolutionComparer);
    115.             int uniqueResolutionCount = 0;
    116.             if (resolutions.Length == 0)
    117.             {
    118.                 // Weird edge case: No resolutions returned
    119.                 uniqueResolutionCount = 1;
    120.                 emptyResolution[0] = Screen.currentResolution;
    121.                 resolutions = emptyResolution;
    122.             }
    123.             else
    124.             {
    125.                 // Count and shuffle unique resolutions to the left
    126.                 for (int i = 1; i < resolutions.Length; i++)
    127.                 {
    128.                     if (resolutionComparer.Compare(resolutions[i], resolutions[uniqueResolutionCount]) != 0)
    129.                     {
    130.                         uniqueResolutionCount++;
    131.                         if (uniqueResolutionCount != i) resolutions[uniqueResolutionCount] = resolutions[i];
    132.                     }
    133.                 }
    134.                 // Account for the first unique resolution
    135.                 uniqueResolutionCount++;
    136.             }
    137.  
    138.             // Find Current Resolution Index
    139.             int currentResolutionIndex = uniqueResolutionCount - 1;
    140.             for (int i = 0; i < uniqueResolutionCount; i++)
    141.             {
    142.                 Resolution resolution = resolutions[i];
    143.                 if (resolution.width == Screen.width && resolution.height == Screen.height)
    144.                 {
    145.                     currentResolutionIndex = i;
    146.                     break;
    147.                 }
    148.             }
    149.  
    150.             // Store Resolution Information
    151.             resolutionInfo.resolutions = resolutions;
    152.             resolutionInfo.resolutionsCount = uniqueResolutionCount;
    153.             resolutionInfo.currentResolutionIndex = currentResolutionIndex;
    154.  
    155.             // TEST
    156.             // string tmp = "Current Index: " + resolutionInfo.currentResolutionIndex + "\n";
    157.             // for (int i = 0; i < resolutionInfo.resolutions.Length; i++)
    158.             // {
    159.             //     Resolution resolution = resolutionInfo.resolutions[i];
    160.             //     tmp += $"{resolution.width} x {resolution.height}\n";
    161.             // }
    162.             // DisplayInfo mainDisplay = displayInfo.displays[displayInfo.currentDisplayIndex];
    163.             // tmp += $"Active Display {mainDisplay.width} {mainDisplay.height}";
    164.             // Debug.Log(tmp);
    165.         }
    166.  
    167.         private void RefreshDisplays()
    168.         {
    169.             // Ensure Display List Initialized
    170.             if (displayInfo.displays == null) displayInfo.displays = new List<DisplayInfo>();
    171.  
    172.             // Load Displays
    173.             Screen.GetDisplayLayout(displayInfo.displays);
    174.             if (displayInfo.displays.Count == 0)
    175.             {
    176.                 Resolution currentResolution = Screen.currentResolution;
    177.                 DisplayInfo fakeDisplay = new DisplayInfo() { name = "Fake display" };
    178.                 fakeDisplay.width = currentResolution.width;
    179.                 fakeDisplay.height = currentResolution.height;
    180.                 fakeDisplay.workArea = new RectInt(0, 0, currentResolution.width, currentResolution.height);
    181.                 fakeDisplay.refreshRate.denominator = 1;
    182.                 displayInfo.displays.Add(fakeDisplay);
    183.             }
    184.  
    185.             // Find Current Display Index
    186.             DisplayInfo currentDisplay = Screen.mainWindowDisplayInfo;
    187.             displayInfo.currentDisplayIndex = 0;
    188.             for (int i = 0; i < displayInfo.displays.Count; i++)
    189.             {
    190.                 DisplayInfo display = displayInfo.displays[i];
    191.                 if (display.Equals(currentDisplay))
    192.                 {
    193.                     displayInfo.currentDisplayIndex = i;
    194.                     break;
    195.                 }
    196.             }
    197.  
    198.             // TEST
    199.             // string tmp = "Current Index: " + displayInfo.currentDisplayIndex + "\n";
    200.             // for (int i = 0; i < displayInfo.displays.Count; i++)
    201.             // {
    202.             //     DisplayInfo display = displayInfo.displays[i];
    203.             //     tmp += $"Monitor {i} - {display.name} - {display.width} x {display.height}\n";
    204.             // }
    205.             // Debug.Log(tmp);
    206.             // Debug.Log($"REFRESH: Main Window Position: [{Screen.mainWindowPosition.x}; {Screen.mainWindowPosition.y}]");
    207.         }
    208.         #endregion
    209.  
    210.         #region Helpers
    211.         private IEnumerator MoveToDisplay(int displayIndex)
    212.         {
    213.             try
    214.             {
    215.                 DisplayInfo display = displayInfo.displays[displayIndex];
    216.                 Debug.Log($"MOVING TO DISPLAY {display.name}");
    217.                 // Target Top Left Corner
    218.                 Vector2Int targetCoordinates = new Vector2Int(0, 0);
    219.  
    220.                 // Move!
    221.                 yield return Screen.MoveMainWindowTo(display, targetCoordinates);
    222.             }
    223.             finally
    224.             {
    225.                 // Update Display Information
    226.                 displayInfo.currentDisplayIndex = displayIndex;
    227.                 // Update Resolution Information
    228.                 RefreshResolutionInformation();
    229.                 // Notify
    230.                 OnDisplayChange(displayInfo);
    231.                 OnResolutionChange(resolutionInfo);
    232.                 // Set Routine Null
    233.                 changeDisplayRoutine = null;
    234.             }
    235.         }
    236.         #endregion
    237.  
    238.         #region Helper Classes and Structs
    239.         public interface IResolutionInformation
    240.         {
    241.             bool IsNextResolution();
    242.             bool IsPrevResolution();
    243.             string DisplayText();
    244.         }
    245.  
    246.         public interface IDisplayInformation
    247.         {
    248.             bool IsNextDisplay();
    249.             bool IsPrevDisplay();
    250.             string DisplayText();
    251.         }
    252.  
    253.         private class ResolutionInformation : IResolutionInformation
    254.         {
    255.             private static readonly Dictionary<(int, int), string> resolutionDisplayTextCache = new Dictionary<(int, int), string>();
    256.  
    257.             public int currentResolutionIndex;
    258.             public int resolutionsCount;
    259.             public int currentWidth => resolutions[currentResolutionIndex].width;
    260.             public int currentHeight => resolutions[currentResolutionIndex].height;
    261.             public Resolution[] resolutions;
    262.  
    263.             public string DisplayText() => GetCachedResolutionText(currentWidth, currentHeight);
    264.             public bool IsNextResolution() => currentResolutionIndex != NextResolutionIndex();
    265.             public bool IsPrevResolution() => currentResolutionIndex != PrevResolutionIndex();
    266.  
    267.             public int NextResolutionIndex()
    268.             {
    269.                 int next = currentResolutionIndex + 1;
    270.                 if (next < resolutionsCount) return next;
    271.                 return currentResolutionIndex;
    272.             }
    273.  
    274.             public int PrevResolutionIndex()
    275.             {
    276.                 int prev = currentResolutionIndex - 1;
    277.                 if (prev >= 0) return prev;
    278.                 return currentResolutionIndex;
    279.             }
    280.  
    281.             private static string GetCachedResolutionText(int width, int height)
    282.             {
    283.                 string text;
    284.                 (int, int) key = (width, height);
    285.                 if (resolutionDisplayTextCache.TryGetValue(key, out text))
    286.                 {
    287.                     return text;
    288.                 }
    289.                 text = $"{width} x {height}";
    290.                 resolutionDisplayTextCache.Add(key, text);
    291.                 return text;
    292.             }
    293.         }
    294.  
    295.         private class DisplayInformation : IDisplayInformation
    296.         {
    297.             private static readonly Dictionary<int, string> monitorDisplayNameCache = new Dictionary<int, string>();
    298.  
    299.             public int currentDisplayIndex;
    300.             public List<DisplayInfo> displays;
    301.  
    302.             public string DisplayText() => GetCachedDisplayText(currentDisplayIndex);
    303.             public bool IsNextDisplay() => currentDisplayIndex != NextDisplayIndex();
    304.             public bool IsPrevDisplay() => currentDisplayIndex != PrevDisplayIndex();
    305.  
    306.             public int NextDisplayIndex()
    307.             {
    308.                 int next = currentDisplayIndex + 1;
    309.                 if (next < displays.Count) return next;
    310.                 return currentDisplayIndex;
    311.             }
    312.  
    313.             public int PrevDisplayIndex()
    314.             {
    315.                 int prev = currentDisplayIndex - 1;
    316.                 if (prev >= 0) return prev;
    317.                 return currentDisplayIndex;
    318.             }
    319.  
    320.             private static string GetCachedDisplayText(int displayIndex)
    321.             {
    322.                 string text;
    323.                 if (monitorDisplayNameCache.TryGetValue(displayIndex, out text)) return text;
    324.                 text = $"Monitor {displayIndex + 1}";
    325.                 monitorDisplayNameCache.Add(displayIndex, text);
    326.                 return text;
    327.             }
    328.         }
    329.  
    330.         private class ResolutionComparer : IComparer<Resolution>
    331.         {
    332.             public int Compare(Resolution x, Resolution y)
    333.             {
    334.                 return (x.width * x.height).CompareTo(y.width * y.height);
    335.             }
    336.         }
    337.         #endregion
    338.     }
    339. }
    340.  
     
    Last edited: Dec 23, 2021
  21. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    Thanks for the report it seems you're right.

    Are you saying that all display infos returned by Screen.GetDisplayLayout have identical sizes even though the monitors aren't identical? Are the monitors duplicated or extended in your case?

    Is there any chance you call Screen.SetResolution with 0 or 1 as width/height? That could cause the window to disappear in fullscreen mode.
     
    VoodooDetective likes this.
  22. VoodooDetective

    VoodooDetective

    Joined:
    Oct 11, 2019
    Posts:
    238
    Thanks very much for your reply!

    1. Are you saying that all display infos returned by Screen.GetDisplayLayout have identical sizes even though the monitors aren't identical? Are the monitors duplicated or extended in your case?
      1. Yes, the API returns identical values for resolution despite the fact that the monitors have very different resolutions. I'm using my second monitor as an extension rather than a duplicate of my main monitor.
    2. Is there any chance you call Screen.SetResolution with 0 or 1 as width/height? That could cause the window to disappear in fullscreen mode.
      1. No, I don't believe so. The only values I ever call Screen.SetResolution with are directly from the results of a call to Screen.resolutions. I use some logic to sort and de-dup that list of resolutions, but I never insert weird values like 0/1.
     
  23. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    I'm not too familiar with our Mac implementation of that API so unfortunately I cannot troubleshoot you further. Could you by any chance file a bug report with a script that reproduces these issues so we could dig in? What you're describing is very unexpected.
     
  24. VoodooDetective

    VoodooDetective

    Joined:
    Oct 11, 2019
    Posts:
    238
    Sure thing! Thanks for taking a look. I just submitted a bug report.
     
  25. VoodooDetective

    VoodooDetective

    Joined:
    Oct 11, 2019
    Posts:
    238
  26. VoodooDetective

    VoodooDetective

    Joined:
    Oct 11, 2019
    Posts:
    238
    I just noticed that the Screen.resolutions API doesn't seem to work on Android. I'm running Unity 2021 and don't see any documentation on that.

    I see an edge case in the DesktopSamples project that catches an empty Screen.resolutions array, but then it just returns a single resolution (current resolution).

    The official guidance seems to suggest reducing resolution on mobile, but without a list of resolutions and with so many wacky aspect ratios out there, what's the best practice?

    Should we just gen up a list based on the Display.main.systemWidth. Like a that decrements by 10% each resolution? Is there some other way we're supposed to go about this? Any advice would be much appreciated. I also checked out the Unity Open Projects Chop Chop game and that doesn't handle the edge case at all. The game would fall flat on its face on android.
     
    Last edited: Mar 16, 2022
  27. DREBOTgamestudio

    DREBOTgamestudio

    Joined:
    Jan 30, 2018
    Posts:
    66
    Hi. I donwload MoveWindowSample. I have 3 monitors. Everything is fine on the main monitor, but on the rest I have problems. My cursor is offset in window mode.
    Everything is fine if full screen mode is enabled.

    11223.gif
     
  28. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    What Unity version is this on?
     
  29. DREBOTgamestudio

    DREBOTgamestudio

    Joined:
    Jan 30, 2018
    Posts:
    66
    I tested it on 2021.3.0f1 and 2022.1.2f1.
     
  30. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    Can you submit a bug report on this? It's definitely unexpected. Please also include your monitor resolution and Windows scaling settings in the report.
     
  31. DREBOTgamestudio

    DREBOTgamestudio

    Joined:
    Jan 30, 2018
    Posts:
    66
    I'll try. I have never done this.
     
  32. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    DREBOTgamestudio likes this.
  33. DREBOTgamestudio

    DREBOTgamestudio

    Joined:
    Jan 30, 2018
    Posts:
    66
    I submitted a bug report. In addition, I checked 3 games. Baldur's Gate 3, The Banner Saga and Disco Elysium. Last game used engine Unity. Other games have custom engines. I have same problem only in game Disco Elysium
     
  34. prakyath_unity

    prakyath_unity

    Joined:
    Dec 11, 2020
    Posts:
    9
    Code (CSharp):
    1. private IEnumerator MoveToDisplay(int index, FullScreenMode mode)
    2.         {
    3.             m_MoveWindowInProgress = true;
    4.  
    5.             try
    6.             {
    7.                 var display = m_Displays[index];
    8.  
    9.                 Debug.Log($"Moving window to {display.name}");
    10.  
    11.                 Vector2Int targetCoordinates = new Vector2Int(0, 0);
    12.                 if (Screen.fullScreenMode != FullScreenMode.Windowed)
    13.                 {
    14.                     // Target the center of the display. Doing it this way shows off
    15.                     // that MoveMainWindow snaps the window to the top left corner
    16.                     // of the display when running in fullscreen mode.
    17.                     targetCoordinates.x += display.width / 2;
    18.                     targetCoordinates.y += display.height / 2;
    19.                 }
    20.  
    21.                 var moveOperation = Screen.MoveMainWindowTo(display, targetCoordinates);
    22.                 yield return moveOperation;
    23.             }
    24.             finally
    25.             {
    26.                 m_MoveWindowInProgress = false;
    27.             }
    28.         }
     
  35. aFeesh

    aFeesh

    Joined:
    Feb 12, 2015
    Posts:
    35
    Classic Unity, even ChatGPT doesn't know how to handle this ridiculous API for changing displays. Fix it.
     
  36. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    huh what? What's wrong with the API?
     
  37. aFeesh

    aFeesh

    Joined:
    Feb 12, 2015
    Posts:
    35
    Display Activate doesn't change monitors just activates another one. https://docs.unity3d.com/ScriptReference/Display.Activate.html

    And Screen.SetResolution doesn't have an option for setting the display either.

    The API should be as easy as something like:
    Code (CSharp):
    1. Screen.SetResolution(width, height, false, refreshRate, displayMonitor);
     
  38. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    You're not supposed to use Display class: it's meant for spawning multiple windows. Use Screen.MoveMainWindowTo instead. That's the API that has been added exactly for this purpose.
     
  39. cuikeqiang

    cuikeqiang

    Joined:
    Nov 16, 2019
    Posts:
    5
    Thanks for your information. I have a question about these saved setting. I am developing Unity app in macOS. Can I delete or change these values with code? I want to change 'Fullscreen mode' and 'Resolution'
     
  40. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    You could delete the player prefs file before the game starts up and/or adding command line parameters passed in to Unity by modifying the game executable (using Create Xcode project build setting).
     
  41. m-guntrum

    m-guntrum

    Joined:
    Mar 14, 2013
    Posts:
    6
    Hi,

    I was eager to try this out to implement a resolution dialog that would also allow our customers to change their target display from within the game. However, on my setup I encounter problems with the second monitor not updating correctly in all scenarios and missing its native resolution in the resolutions list.

    I used the linked repository: https://github.com/Unity-Technologies/DesktopSamples/tree/master/MoveWindowSample and created a minified version from it to try things out in Unity 2022.3.8f1

    Here is some video evidence of what is happening:


    Things to note:
    - I use identical monitor models
    - We have received customer reports on earlier shipped titles, where people were unable to select their native monitor resolution. We created a workaround by always adding the native resolution to the default resolution list provided by Unity. Still, seeing this behavior when changing displays, remind me of these reports and might hint that something regarding the detected resolutions and multi monitor setups is not working as expected for some time now, since these customer reports date back to 2020.

    I created a bug report for this case number is: IN-59007
     
    Last edited: Oct 28, 2023
  42. m-guntrum

    m-guntrum

    Joined:
    Mar 14, 2013
    Posts:
    6
    I have investigated further and the issues are related to the way monitors can be setup in Windows. I have two monitors and an AV receiver I use for audio output that is also connected via HDMI and treated by windows as a monitor. In windows you can choose to "duplicate" two screens into one. This is how it was setup on my machine and this setup results in unity recognizing two monitors and having the faulty behavior just as in the video above.

    If I instead "extend" this into 3 displays, Unity will report 3 monitors and output the correct resolutions on each of them.
    It looks like when duplicating screen 2 and 3 into one, Unity recognizes only monitor 3 which results into the weird glitches and missing resolutions.

    Here is another video showing the different setups and what this does with the resolution panel.


    So, I understand this might complicate things to reproduce this, but I guess this "merging" display feature in windows would need to get investigated to ensure the resolution settings work as expected.
     
  43. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,645
    Thanks for your investigation on your end! We will take a look.