Search Unity

How to: Multi-Display Resizing Display #2

Discussion in 'Scripting' started by BisuDagger, Apr 18, 2016.

  1. BisuDagger

    BisuDagger

    Joined:
    May 2, 2014
    Posts:
    31
    Hello, I am using 2 displays in my current sim. I edited my code here so it is more readable for my question. Basically I can full screen and window mode Display 1 fine. But how do I do the same for display #2. I think I don't understand Display functions correctly. If anyone has the time to break down how Displays resizing works, it'd be much appreciated!

    Display 1 logic:
    Code (csharp):
    1.  
    2.     resolution = Screen.currentResolution;
    3.     if (isFullScreen == true)
    4. {
    5.        // when toggle back to fullscreen, keep the original resolution
    6.        Screen.SetResolution(resolution.width, resolution.height, true);
    7. }
    8.    else
    9. {
    10.       Vector2 currentResolution = new Vector2(referenceResolution.x * scaleSizePercent, referenceResolution.y * scaleSizePercent);
    11.       Screen.SetResolution((int)currentResolution.x, (int)currentResolution.x, false);
    12. }
    13.  
    Display 2 logic integrated in:
    Code (csharp):
    1.  
    2.     resolution = Screen.currentResolution;
    3.  
    4. void Awake()
    5. {
    6.   if (Display.displays.Length > 1)
    7.   {
    8.   Display.displays[1].Activate(Screen.width, Screen.height, 0);
    9.   Display.displays[1].SetRenderingResolution(resolution.width, resolution.height);
    10.   }
    11. }
    12.     if (isFullScreen == true)
    13. {
    14.        // when toggle back to fullscreen, keep the original resolution
    15.        Screen.SetResolution(resolution.width, resolution.height, true);
    16.        Display.displays[1].SetRenderingResolution(resolution.width, resolution.height);
    17. }
    18.    else
    19. {
    20.       Vector2 currentResolution = new Vector2(referenceResolution.x * scaleSizePercent, referenceResolution.y * scaleSizePercent);
    21.       Screen.SetResolution((int)currentResolution.x, (int)currentResolution.x, false);
    22.      Display.displays[1].SetParams((int)currentResolution.x, (int)currentResolution.x, 0, 0);
    23. }
    24.  
     
    Last edited: Apr 18, 2016
  2. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
  3. BisuDagger

    BisuDagger

    Joined:
    May 2, 2014
    Posts:
    31
    LiterallyJeff likes this.
  4. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    Are you having issues in Editor or with the game build? One thing to note is that Display.displays[] returns length with only one entry in editor. so you have to use #if UNITY_EDITOR to stop the code for multi-display when you Play Mode test. Just use the multiple GAME tabs and set which DISPLAY1 and DISPLAY2 for each.

    Display.displays[1].Activate() is an overload. you can call it with out any arguments. Which will capture that display at the native resolution and refresh rate.

    Once Display.displays[1].Activate() you can not release, then recapture it in game. It is a one time event.

    I also find that resolution = Screen.currentResolution; returns the information ONLY for Display.Main! Not the secondary one.
     
  5. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    One of are product lines is actually a off screen touch screen panel for Unity. So I am working with similar issues to you. I am currently waiting on UPS to delivery are first Development Kit version of one of are products for testing.

    I have found that the multi-display system in Unity is rather lacking in features. I have actually ended up overloading the entire DISPLAY class with my own DISPLAY class to give me all the tools I need. I am currently working on that code.

    Feel free to reach out to me directly, I'd be willing when off work to help you to see how you can get your code up and running.
     
  6. BisuDagger

    BisuDagger

    Joined:
    May 2, 2014
    Posts:
    31
    Thanks for the feedback Broten! So to answer some of your questions:
    1. Everything works fine in the editor and I am using #if UNITY_EDITOR
    2. I have two of the same monitors and resolution so I was hoping to cheat results by using Screen data for display #2
    3. To satisfy the needs of my program I need to have display #2 be full screen and fill the display properly. However when I toggle display 1 it resizes display #2 which is why I was trying to make toggle sizing for display #2 work. If I could just force display #2 to fullscreen for the duration of the player that'd be perfectly acceptable.
    4. User testing makes finding a solution time sensitive, but down the road overloading the Display class sounds like a good idea.

    My Questions:
    1. What should me default settings be for display #2 if I want to be max full screen at launch? (This needs to work on a projector plugged into a laptop for instructor use)
    2. Is there a way to test this faster then making 10 minute builds every time a change a line of code?
     
  7. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    Tested
    Code (CSharp):
    1.  
    2. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    3. static void OnAfterSceneLoadRuntimeMethod()
    4.         {
    5. if (Display.displays.Length > 1)
    6. {
    7.   Display.displays[1].Activate();
    8.   Display.displays[1].SetRenderingResolution(resolution.x, resolution.y);
    9. }
    10.  
    11. }
    Found that Display.displays[1].Activate(); was best, full screens the displays using (x,y,r).
    Should note that multi displays are boarder-less windows mode.

    Unity UI was an issue. Screen Space Overlay captures data from the primary display when setting up the UI on secondary. So things get out of position and scale etc.

    I set the Secondary Displays UI to Screenspace Camera instead. and ensured that camera 2 existed and had DISPLAY2 set.

    SetRenderingResolution appears to have no effect in Fullscreen Boarder-less Multi-Windowed Mode, from my knowledge of Windows.forms. I expected that.

    Each Windows is rendering at the resolution defined for it's size in Activate(). If you one as 1280x720p on a 2650x1440p display it will not be full screen and only covers part of the display. This is why I suggested use Display.displays[1].Activate(); not the overload method.

    If you wish to be full screen but lower resolution. Try outputting to a renderTexture of a size lower x,y then display. Then draw that texture stretched to the full x,y resolution.
     
    Last edited: Apr 18, 2016
  8. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    I am off work in 15 minutes, If you are willing to share the project or a sample project I can give you an idea on exactly what is going wrong.

    Code (CSharp):
    1. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    2. static void OnAfterSceneLoadRuntimeMethod()
    3.         {
    4. if (Display.displays.Length > 1)
    5. {
    6.   Display.displays[1].Activate();
    7. }
    Should give you a full screen secondary display. Just place it in a script it will auto execute.
     
  9. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    Code (CSharp):
    1. Display.displays[1].SetParams((int)currentResolution.x, (int)currentResolution.x, 0, 0);
    This is wrong! You are setting (x,y,0,0) This is set to move the Display2 to origin. You would need to set the correct Windows Virtual Desktop co-ordinates.

    This is in part why I'm overloading Display class. I'm writing code that gives me more data on the display windows. such as current location, friendly name, and vendor name.

    With windows virtual desktop co-ordinate system:

    If you have 2 displays that are 1080p. and Display2 is directly to the Right of Display1.
    Then Display 1 is (1920,1080,0,0)
    and Display 2 is (1920,1080,1920,0).
    But users can place screens in the Windows Settings in any layout they want so you can not presume locations ever.​

    Also don't bother changing the Display 1 or 2 from it's resolution. Keep them fullscreen at native resolution. You can scale UI Canvas' to desired resolution using Canvas Scaler. for the Gameview send it to a renderTexture of the desired resolution. Then use a RawImage in UI and place the renderTexture behind the other UI elements. But this would not allow clicks to world space UI items if using them. (a complex pass thru of the Raycasts would be needed).
     
  10. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    Hope some of this helps you with your project.

    My Display class overload is currently to full of bugs and not ok to share yet. But provides some of the things you looked for. Are scripts automatically detect the presence and settings of touch device. At runtime sets up the Orbnet TouchPad Device on a PC or GamePad on Wii U. Else enabled standard single display mode.

    It will eventually be released as part of are Orbnet TouchPad Devices Asset package. Designed for using are Orbnet Touch panels products for PC gaming.

    PC requirements
    • Unity 5.4.0B15 Beta or higher
    • Orbnet TouchPad DevKit or
    • Orbnet TouchPad Pro DevKit

    Wii U requirements
    • Unity for Wii U 3.1.1 (Unity 5.3.3p2)
    Editor:
      • Orbnet TouchPad DevKit or
      • Orbnet TouchPad Pro DevKit
    Build on: Nintendo Wii U Game System
      • CAT-DEV DevKit
      • DRC-DK (Wii U Development GamePad)
     
    Last edited: Apr 19, 2016
  11. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,297
    Note we disabled multi screen support for some platforms for the UI system in 5.3(see release notes). It had some issues with none native resolution which cause it to break even in single screen mode. We are hoping to get it sorted but no eta yet. If you want to try it anyway then 5.3.0f1 is the only version with it enabled.
     
  12. BisuDagger

    BisuDagger

    Joined:
    May 2, 2014
    Posts:
    31
    @Broten,
    The great news:
    Thank you so much for the feedback once again. I decided to force fullscreen when the application has multiple displays and it works perfectly. The toggling of Display 1 cause to much issue and now I only allow that to happen when only one display is connected.

    Follow up question:
    I use no UI/Canvas for Display 2 and only use 3D space and an orthographic camera. This all works great. However, when I leave the scene, the last frame of the Display 2 camera remains on the Display. Is there a way to force refresh display 2 so it will return to black when no graphics are being sent to this? There are plenty of workarounds for this, but I'm curious if you found a solution to this.

    @Karl,
    I decided I could get away with out a Display 2 Canvas and it would solve my problems. Since I'm using schematics I need to have display 2 with a full screen version and display one with a small scale version. To accomplish this without two duplicate objects, I use a render texture watching the object that would be on display 2 and project to a raw image that is under the canvas of display 1.
     
  13. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    @BisuDagger The window is still present you need to always output 'something' even if you don't want anything on it yet.

    What I do is leave my display gameObjects and scripts in scene, using a do not destroy. Which lets them stay in scene when loading the next one. Then I detect for each display if they have an incoming source, or flagged by user input to not use them. If it currently is not in use I send it a predefined renderTexture with a black surface or display a logo to that display.

    @karl.jones Yes I noticed how multi.screen support is a tad broken. Scaling issues in screen space overlay, recieving Input events etc. Been constantly following Unity UI and Multi-Display PC Beta builds. And in the Unity for Wii U 3.1.1 (Unity 5.3.3p2) Unified Editor.

    Suggestion as Multi-display develops, you should add to Display class some extra needed info for scripting multi-display
    .
    Example public void SetParams(int width, int height, int x, int y);
    If you wish to set this the x,y of the current window location is not exposed currently in the Display.displays[] list.

    I had to add extra info using the [user32.dll] from the user32 enum
    I created BrotenTech.Display.displayinfo[] list with

    Code (CSharp):
    1.         public struct DisplayInfoStruct
    2.         {
    3.             public string Availability { get; set; }
    4.             public int CurrentScreenHeight { get; set; }
    5.             public int CurrentScreenWidth { get; set; }
    6.             public Rect MonitorArea { get; set; }
    7.             public int MonitorTop { get; set; }
    8.             public int MonitorBottom { get; set; }
    9.             public int MonitorLeft { get; set; }
    10.             public int MonitorRight { get; set; }
    11.             public string DeviceName { get; set; }
    12.             public string FriendlyName { get; set; }
    13.             public string VendorName { get; set; }
    14.             public string ACPIvendorID { get; set; }
    15.             public string ACPIproductID { get; set; }
    16.         }
     
    Last edited: Apr 19, 2016
    karl_jones likes this.
  14. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    @karl.jones Would also be really handy for Multi-Display projects if in Editor the GameView had not just a Maximize in Editor Window. But also a Maximize Fullscreen on Display[x]. So that say Display 2 could be viewed at 1920x1080 boarder-less on \.\\DISPLAY2

    So far what I have been doing is making a Editor PopUp boardless with a renderTexture of \.\\DISPLAY[x] of course this is great for checking the appearance but does have the drawback of not allowing UI inputs.

    Edit: I was actually contemplating being bored enough to script a code to capture the location of the mouse from Windows OS and then correlating that location to the actual UI canvas of the game being renderTextured. Casting my own rays. but not sure if I am that bored or have free time yet.
     
    Last edited: Apr 19, 2016
  15. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    @BisuDagger Glad my info helped you figure out a solution for your project! Ask me anytime if you need more help with Multi-Display Mode.
     
  16. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    @karl.jones Not sure if it is documented. But I noticed the Input.mousePosition when moving to a new display X value resets to 0 origin but y stay reflecting the actual value compared to all displays.

    Example
    top right display 1 could be 2560 x1440. Top Left of display 2 is 0,1440. Top Right display 2 800,1440.
    As you see the 0 value should be 2561 and 800 should be 3360.

    Presume this is similar to the issue that causes the canvas stretching issues. :p
     
  17. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,297
    I'm not aware of that. Could you file a bug report please so It goes to someone who knows more about this area :)
     
  18. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    Will do later tonight.

    I also a few minutes ago discovered that windows10 if one of the displays is touch enabled, leaves the gestures and Hotspots active. Additionally adds distorts to the vector2 of cursor location aswell. I will need to. BUG this forsure once got the time.
     
    karl_jones likes this.
  19. BrotenStudios

    BrotenStudios

    Joined:
    Feb 13, 2014
    Posts:
    155
    Got bored enough that I just wrote MultiDisplay.Mouse(x,y,d) class that gives a X, Y, and DISPLAY value for mouse pointer.
    and then a script can trigger a Raycasts for correctly to a canvas.

    End result is MultiDisplays with canvas screenspace or worldspace is working.
     
    karl_jones likes this.
  20. BisuDagger

    BisuDagger

    Joined:
    May 2, 2014
    Posts:
    31
    @BrotenTechCorp ,
    I've been playing with Display 2 input today. Some interaction has been deemed important to the customer.
    My hypothesis was:
    Code (csharp):
    1.  
    2.   if (Input.GetMouseButtonDown(0))
    3.   {
    4.   print("Screen width " + Screen.width);
    5.   print("mousePosition.x " + Input.mousePosition.x);
    6.   if (Input.mousePosition.x > Screen.width)
    7.   {
    8.   print("hit second display");
    9.   }
    10.   }
    11.  
    Result: Input.mousePosition.x never gets larger then Screen.width

    So my question is, how were you able to find the second display?
     
  21. BisuDagger

    BisuDagger

    Joined:
    May 2, 2014
    Posts:
    31
    Update: I am able to use
    Code (csharp):
    1.  
    2. var mousePos = Display.RelativeMouseAt (Input.mousePosition);
    3. GUILayout.Label ("mouse position:" + ((Vector2)mousePos).ToString());
    4. GUILayout.Label("display id:" + mousePos.z);
    5. GlobalInputVariables.fCurrentDisplay = mousePos.z;
    6.  
    I did realize that this will only work if I'm running off a build and I do store what current display I am on. The next step from here is to get proper detection working. This is what I am running for input.

    Code (csharp):
    1.  
    2.   void Awake()
    3.   {
    4.       m_inputAreaEventTrigger.AddListener(EventTriggerType.PointerDown, OnPointerDown);
    5.   }
    6.  
    7.   void OnPointerDown(BaseEventData data)
    8.   {
    9.       RaycastHit hit;
    10.       Ray ray = display2Cam.ScreenPointToRay(Input.mousePosition);
    11.       if (Physics.Raycast(ray, out hit))
    12.       {
    13.              if (hit.transform == transform && GlobalInputVariables.fCurrentDisplay == 1)
    14.             {
    15.                 textObj.text = "hit from cam 2";
    16.                 print("hit second display");
    17.                 return;
    18.             }
    19.        }
    20.   }
    21.  
     
  22. Little-Big-Monkey

    Little-Big-Monkey

    Joined:
    Mar 4, 2014
    Posts:
    40
    I know it's necro-posting, but after lot of googling, this post have the most information on this bug.
    This is still an issue in Unity 2019, when you have 2 or more displays with different resolution.

    Display report the correct system size and rendering size for each display.
    The canvas also report the correct size on each screen.
    BUT, the canvas is strech to the main display ratio from the top left corner, when set on another display,
    This happen even if canvas is set to Screen space - Camera, World space, not only Screen space - Overlay.

    See the screenshot in attachment. The blue square is supposed to be squared and centered on the display.

    Annotation 2019-08-22 222643.png

    After lot of testing, the only solution I found, is to use a dummy gameobject to counter-scale the canvas like this :

    Code (CSharp):
    1. transform.localScale = new Vector3((float)Display.displays[i].systemWidth / Display.displays[0].systemWidth, (float)Display.displays[i].systemHeight / Display.displays[0].systemHeight, 1.0f);
    2.  
    Hope this can help to fix it ...
     
  23. jingshuihi

    jingshuihi

    Joined:
    May 27, 2019
    Posts:
    1
    Hello, now I want to connect one host to two monitors. Monitor 1 is the main monitor and monitor 2 is the extended monitor.When setting the resolution with setparams, screen repetition occurs in monitor 2. Is there any solution?Thank you very much!
     
  24. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,297
    Im not sure I understand. Can you provide more information.
    What is the resolution of both monitors?
    What resolution are you settling them to in game?
    Can you provide a picture of what you mean by repetition?