Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. We are looking for feedback on the experimental Unity Safe Mode which is aiming to help you resolve compilation errors faster during project startup.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Switch Monitor at runtime

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

  1. InvincibleCat

    InvincibleCat

    Joined:
    Dec 23, 2014
    Posts:
    69
    Hey!

    I have the option the change which monitor you want to use at runtime from a video setting menu on PC.
    It worked fine so far. I am using this method to do so:
    Code (CSharp):
    1.     public IEnumerator ChangeMonitorAsync()
    2.     {
    3.         if (Monitor >= Display.displays.Length)
    4.         {
    5.             Monitor = 0;
    6.         }
    7.         PlayerPrefs.SetInt("UnitySelectMonitor", Monitor);
    8.         Screen.SetResolution(800, 600, FullScreen);
    9.         yield return null;
    10.         Resolution = Screen.resolutions.Length - 1;
    11.         Apply();
    12.     }
    Since I updated to Unity 2017.1 it does not work anymore. I have to restart the game to apply the changes.

    It seems that it is a known issue but it says won't fix...
    https://issuetracker.unity3d.com/is...ntime-with-unityselectmonitor-no-longer-works

    Any clue?

    Thanks,
    -Tim
     
  2. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,495
  3. InvincibleCat

    InvincibleCat

    Joined:
    Dec 23, 2014
    Posts:
    69
    Unfortunately this won't work as it adds a new display (a secondary one). It does not change the main display!
     
    futurlab_xbox likes this.
  4. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,495
    Yeah you're right. Seems like they need a Deactivate method to counter. My guess is they won't fix it because something is slated to support the functionality properly (not setting magic strings in PlayerPrefs)
     
  5. InvincibleCat

    InvincibleCat

    Joined:
    Dec 23, 2014
    Posts:
    69
    Maybe... it is kind of annoying though since it broke my game...
    Last resort for me would be to tell the user to restart the game then
     
  6. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    7,501
    The reason I marked it as won't fix was because it was relying on an undocumented internal implementation detail (setting the UnitySelectMonitor player pref) of the engine. It broke by accident as the whole windowing manager was rewritten to fix some serious design flaws related to multi-monitor setups. Since this wasn't a documented or supported feature, it was never noticed when the build was tested. And I have no idea how this was discovered to achieve wanted effect in the first place... Furthermore, "fixing" this to behave like it did before would make moving the window across monitors and entering/leaving fullscreen on a particular monitor broken.

    We don't have any APIs to manipulate window position in Unity, but you can P/Invoke into MoveWindow function instead and manipulate the window position with it.
     
  7. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    4,992
    What is the recommended and supported Unity API to switch monitors in Unity 2017 while the game is running?
     
    JimmyCushnie likes this.
  8. InvincibleCat

    InvincibleCat

    Joined:
    Dec 23, 2014
    Posts:
    69
    I understand. In the meantime I just change it so a restart is required.
     
  9. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    7,501
    Use the Win32 function I mentioned.
     
  10. InvincibleCat

    InvincibleCat

    Joined:
    Dec 23, 2014
    Posts:
    69
    But how can we get the window position then?
     
  11. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    7,501
    You can use EnumDisplayMonitors function to find coordinates of all monitors in the virtual desktop space.
     
  12. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    4,992
    This functionality is needed in the majority of Desktop games. Games often provide an option to select the monitor in which the game is running via an options screen of the game, as shown in the screenshot below.

    video options.jpg

    Do you think it would be beneficial if a multi platform game engine supports such functionality?

    Having support for that allows game developers to write code for switching monitors once and it works across all platforms, such as Windows, OS X and Linux, versus everyone needs to implement it from scratch and has to deal with platform specifics.
     
    Last edited: Jan 29, 2020
    DaDonik likes this.
  13. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    7,501
    Yes, I definitely believe so. ScreenManager improvements in general are in my team (desktop team) roadmap. Being realistic though, we don't support that today, so I mentioned that API as a way to solve your issues now, rather than at some unspecified time in the future.
     
    Peter77 likes this.
  14. HarryCodder

    HarryCodder

    Joined:
    Feb 20, 2015
    Posts:
    68
    Hi @Tautvydas-Zilys

    Do you have any news on progress on this feature ?
    Thanks in advance.
     
    Tom60chat likes this.
  15. Hyp-X

    Hyp-X

    Joined:
    Jun 24, 2015
    Posts:
    292
    Is there any improvement for this?
    Thanks in advance.
     
    SuperNeon likes this.
  16. markv12

    markv12

    Joined:
    Dec 31, 2013
    Posts:
    22
    With the release of 2019.3 the resolution dialogue has been removed meaning this feature is needed more than ever.
    Now there is no way to specify which screen your app runs on besides changing your main display in Windows.
     
  17. JimmyCushnie

    JimmyCushnie

    Joined:
    Jun 7, 2017
    Posts:
    132
    @markv12 The user can use OS hotkeys to switch monitors, or switch the application to windowed mode and drag the window over to a different monitor before going back to fullscreen. However, this solution is far from ideal; in particular, Display.main does not update, so we cannot get an updated list of supported resolutions to present to the user. I hope @Tautvydas-Zilys and the rest of the desktop team get around to adding this functionality soon.

    The Win32 API mentioned in this thread does not work on Mac or Linux, so it's not an option for cross-platform games.
     
  18. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    7,501
    Screen.resolutions should update. They did last time I checked...
     
  19. VoodooDetective

    VoodooDetective

    Joined:
    Oct 11, 2019
    Posts:
    114
    @Tautvydas-Zilys is there a roadmap we could follow? It's pretty rough on multi-monitor users to have to go out of fullscreen, drag, and go back into fullscreen every time they play.

    Trying to figure out how much time to put into this. If there's a feature upcoming in the next year, I'll hold off. Otherwise, I'll have to do the cross-platform code myself through p/invoke which will take lots of time and testing.
     
    JimmyCushnie likes this.
  20. JimmyCushnie

    JimmyCushnie

    Joined:
    Jun 7, 2017
    Posts:
    132
    I really, really wish Unity was open source. There are so many tiny issues like this... with open source, we could fix them ourselves, and Unity would be better for everyone.
     
  21. Holy-Manfred

    Holy-Manfred

    Joined:
    Nov 30, 2013
    Posts:
    11
    Since it has been almost three years since the question has been asked: Is there any progress on this?
    Code (CSharp):
    1. PlayerPrefs.SetInt("UnitySelectMonitor", Monitor);
    still works but requires a restart, which is not ideal.
    @Tautvydas-Zilys Could you provide a bit of information and let us know if this is something that will ever get addressed?
     
    futurlab_xbox likes this.
  22. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    7,501
    This is on my team's backlog... I am trying to make it happen.
     
  23. talecrafter

    talecrafter

    Joined:
    Mar 26, 2013
    Posts:
    34
    Oh, this is cool to hear.

    And I don't want to hijack this, but if the code that has to be touched for making switching monitors possible is also the part where it's responsible that we can activate more displays but not deactivate them, a fix for that would be most welcome too.
     
    futurlab_xbox likes this.
  24. cfloutier

    cfloutier

    Joined:
    Jul 30, 2009
    Posts:
    34
    yes most wanted since the dialog box disapeared in version 2019.3...;
    It's hard to ask for the user to restart the application
     
  25. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    113
    A very desired feature
     
  26. Weightless

    Weightless

    Joined:
    Sep 7, 2017
    Posts:
    13
    yeah would be nice to see this
     
  27. Weightless

    Weightless

    Joined:
    Sep 7, 2017
    Posts:
    13
    Alright so for anyone interested I made this work in 2019.3 using the stuff recommended by Tautvydas-Zilys

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Runtime.InteropServices;
    5. using UnityEngine;
    6.  
    7. public class DisplayChanger : MonoBehaviour
    8. {
    9.     //cycles through the connected displays by calling the ChangeDisplayClicked() method
    10.     //does not work with windowed mode
    11.  
    12.     List<DisplayInfo> myDisplays = new List<DisplayInfo>();
    13.     List<MyMonitor> myMonitors = new List<MyMonitor>();
    14.     int monitorNumber = 0;
    15.  
    16.     [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, ExactSpelling = true, SetLastError = true)]
    17.     internal static extern void MoveWindow(IntPtr hwnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
    18.  
    19.     [DllImport("user32.dll")]
    20.     private static extern IntPtr GetActiveWindow();
    21.  
    22.     [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, ExactSpelling = true, SetLastError = true)]
    23.     internal static extern bool GetWindowRect(IntPtr hWnd, ref RECT rect);
    24.  
    25.     [DllImport("user32.dll")]
    26.     static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lprcClip, MonitorEnumDelegate lpfnEnum, IntPtr dwData);
    27.  
    28.     delegate bool MonitorEnumDelegate(IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData);
    29.  
    30.     [StructLayout(LayoutKind.Sequential)]
    31.     public struct RECT
    32.     {
    33.         public int left;
    34.         public int top;
    35.         public int right;
    36.         public int bottom;
    37.     }
    38.  
    39.     public class DisplayInfo
    40.     {
    41.         public string Availability { get; set; }
    42.         public string ScreenHeight { get; set; }
    43.         public string ScreenWidth { get; set; }
    44.         public RECT MonitorArea { get; set; }
    45.         public RECT WorkArea { get; set; }
    46.     }
    47.  
    48.     public class DisplayInfoCollection : List<DisplayInfo>
    49.     {
    50.     }
    51.  
    52.     [DllImport("User32.dll", CharSet = CharSet.Auto)]
    53.     public static extern bool GetMonitorInfo(IntPtr hmonitor, [In, Out] MONITORINFOEX info);
    54.     [DllImport("User32.dll", ExactSpelling = true)]
    55.     public static extern IntPtr MonitorFromPoint(POINTSTRUCT pt, int flags);
    56.  
    57.     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
    58.     public class MONITORINFOEX
    59.     {
    60.         public int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));
    61.         public RECT rcMonitor = new RECT();
    62.         public RECT rcWork = new RECT();
    63.         public int dwFlags = 0;
    64.         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    65.         public char[] szDevice = new char[32];
    66.     }
    67.  
    68.     [StructLayout(LayoutKind.Sequential)]
    69.     public struct POINTSTRUCT
    70.     {
    71.         public int x;
    72.         public int y;
    73.         public POINTSTRUCT(int x, int y)
    74.         {
    75.             this.x = x;
    76.             this.y = y;
    77.         }
    78.     }
    79.  
    80.     public DisplayInfoCollection GetDisplays()
    81.     {
    82.         DisplayInfoCollection col = new DisplayInfoCollection();
    83.  
    84.         EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
    85.             delegate (IntPtr hMonitor, IntPtr hdcMonitor, ref RECT lprcMonitor, IntPtr dwData)
    86.             {
    87.                 MONITORINFOEX mi = new MONITORINFOEX();
    88.                 mi.cbSize = (int)Marshal.SizeOf(mi);
    89.                 bool success = GetMonitorInfo(hMonitor, mi);
    90.                 if (success)
    91.                 {
    92.                     DisplayInfo di = new DisplayInfo();
    93.                     di.ScreenWidth = (mi.rcMonitor.right - mi.rcMonitor.left).ToString();
    94.                     di.ScreenHeight = (mi.rcMonitor.bottom - mi.rcMonitor.top).ToString();
    95.                     di.MonitorArea = mi.rcMonitor;
    96.                     di.WorkArea = mi.rcWork;
    97.                     di.Availability = mi.dwFlags.ToString();
    98.                     col.Add(di);
    99.                 }
    100.                 return true;
    101.             }, IntPtr.Zero);
    102.         return col;
    103.     }
    104.  
    105.     public class MyMonitor
    106.     {
    107.         public int targetX;
    108.         public int monitorNumber;
    109.         public int height;
    110.         public int width;
    111.  
    112.         public MyMonitor(int targetX, int monitorNumber, int height, int width)
    113.         {
    114.             this.targetX = targetX;
    115.             this.monitorNumber = monitorNumber;
    116.             this.height = height;
    117.             this.width = width;
    118.         }
    119.     }
    120.  
    121.     private void Start()
    122.     {
    123.         myDisplays = GetDisplays();
    124.         for (int i = 0; i < myDisplays.Count; i++)
    125.         {
    126.             myMonitors.Add(new MyMonitor(myDisplays[i].WorkArea.left, i, Convert.ToInt32(myDisplays[i].ScreenHeight), Convert.ToInt32(myDisplays[i].ScreenWidth)));
    127.         }
    128.     }
    129.  
    130.     public void ChangeDisplayClicked()
    131.     {
    132.         if (monitorNumber < myDisplays.Count)
    133.         {
    134.             StartCoroutine(MyMoveWindow(myMonitors[monitorNumber].height, myMonitors[monitorNumber].width, myMonitors[monitorNumber].targetX));
    135.             monitorNumber++;
    136.         }
    137.         else if (monitorNumber >= myDisplays.Count)
    138.         {
    139.             monitorNumber = 0;
    140.             StartCoroutine(MyMoveWindow(myMonitors[monitorNumber].height, myMonitors[monitorNumber].width, myMonitors[monitorNumber].targetX));
    141.         }
    142.     }
    143.  
    144.     public IEnumerator MyMoveWindow(int newHeight, int newWidth, int targetX)
    145.     {
    146.         IntPtr hwnd;
    147.         RECT Rect = new RECT();
    148.         yield return new WaitForSeconds(2);
    149.         hwnd = GetActiveWindow();
    150.         GetWindowRect(hwnd, ref Rect);
    151.         MoveWindow(hwnd, targetX, Rect.top, newWidth, newHeight, true);
    152.     }
    153. }
    154.  
     
unityunity