Search Unity

  1. Unity 2019.1 is now released.
    Dismiss Notice

Constrain Mouse Cusor While Fullscreen (Windows)

Discussion in 'Wish List' started by MatthewW, Dec 5, 2006.

  1. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,355
    Please cage the mouse cursor to the render window while fullscreen on Windows. Otherwise, multi-monitor users can move their cursor off the game and click somewhere else (which shifts focus to whatever app/window they accidentally click on). Constraining the mouse cursor to one monitor is expected behavior for a fullscreen game.

    ClipCursor is the relevant function here...

    It's mostly a development annoyance, true, but more and more end-users are ending up with multiple monitors these days too.
     
  2. NCarter

    NCarter

    Joined:
    Sep 3, 2005
    Posts:
    686
    Mac OS X also has this problem, although when all screens are captured, you can't accidentally click on other apps. However, as the mouse pointer may be floating around in space outside of the main screen, you might sometimes lose track of where it is.
     
  3. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,355
    Ah, interesting (I'm new to MacOS myself).

    The evil with Windows is that you can actually click on another app, just as if you had switched to it with alt-tab. IE, the game will minimize--it's really annoying.
     
  4. Aras

    Aras

    Graphics Plumber Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,528
    Good point. Adding to the todo list.
     
  5. techbinge

    techbinge

    Joined:
    Jul 22, 2006
    Posts:
    88
    couldn't the mouse be constrained w/ some simple script? Getting the dimensions and origin of window and then ensuring the mouse stays within? I like to see the mouse fixed to center of screen where the cross hairs would be.
     
  6. MatthewW

    MatthewW

    Joined:
    Nov 30, 2006
    Posts:
    1,355
    Someone correct me if I'm wrong, but I don't think Unity lets you set the position of the system mouse cursor through the script (you can hide the real mouse cursor and move around your own GUITexture, but that's different).
     
  7. Jei

    Jei

    Joined:
    Jan 9, 2007
    Posts:
    2
    I noticed this problem also when running in window mode (browser and non-browser versions); moving the mouse while outside the game window will still alter your viewing direction.

    Having the mouse auto lock at a central point and simply moving the mouse to change the view would be great for FPS, but for other types of games, a click and move function (simular to WoW) will be handy for changing the view. Where clicking the mouse will lock the pointer at that location and you can rotate your view; once the mouse is released, the cursor is visable/movable again.
     
  8. greenland

    greenland

    Joined:
    Oct 22, 2005
    Posts:
    205
    On the topic of WoW and windows and alt+tab...
    WoW has a bug in which if you alt+tab to another program while a key is depressed (foward run, for instance), your character continues to run as if the key was still depressed, even after you tab back to WoW. The naural response to running foward involuntarily is to run back, but the only way to stop it is to depress the foward key again, then release it and only then does it stop.

    Long story short, is there a way that unity can send a signal when it gains and loses window focus to prevent such issues?
     
  9. lgoss007

    lgoss007

    Joined:
    Dec 6, 2006
    Posts:
    88
    Hmm, I always considered that a feature as I could im or email someone while running to a location, :wink:
     
  10. taoa

    taoa

    Joined:
    Dec 10, 2009
    Posts:
    88
    It's been over three years now.

    Anyone took the time to look into this?

    :wink:
     
  11. taoa

    taoa

    Joined:
    Dec 10, 2009
    Posts:
    88
    Guess not!

    :roll:
     
  12. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    It is possible to control the cursor's position - but I know how to do it only for Windows and only for standalone (and also only for PRO as far as I understand it).
     
  13. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    Something like that (borrowed the code from Neodrop - thanks, man!)

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Runtime.InteropServices;
    4.  
    5. public class NeoMouse : MonoBehaviour
    6. {
    7.     [DllImport("<span class="posthilit">user32</span>.dll", CharSet = CharSet.Auto, SetLastError = true)]
    8.     public static extern int SetCursorPos(int x, int y);
    9.  
    10.     public Vector2 mousePos;
    11.  
    12.         void Update ()
    13.     {
    14.         SetCursorPos((int)mousePos.x, (int)mousePos.y);
    15.         }
    16. }
     
  14. taoa

    taoa

    Joined:
    Dec 10, 2009
    Posts:
    88
    Yep, I ended up doing the same thing.
    However, as the system updates the cursor's position before Unity (obviously ^^), you end up with your mouse going outside the frame for a split second before it gets positioned back inside your game window. This still allows for the user to click outside the game.

    I've been trying to use
    Code (csharp):
    1. [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )]
    2. public static extern bool ClipCursor( ref RECT rcClip );
    3.  
    to block the cursor inside the game frame at a system level, but even if the call to ClipCursor is successful, the function has no effect whatsoever.
     
  15. taoa

    taoa

    Joined:
    Dec 10, 2009
    Posts:
    88
    An alternative:

    Find System.Windows.Forms.dll and System.Drawing.dll from the MonoCompiler.framework folder inside your Unity's installation folder (mine was at C:\Program Files (x86)\Unity\Editor\Data\MonoCompiler.framework) and copy them inside your project's Assets folder (it doesn't have to be directly there, make a subfolder to keep things tidy ^^), then in your code you can add this directly:

    Code (csharp):
    1. System.Windows.Forms.Cursor.Position = new System.Drawing.Point( x, y );
    (it might be necessary to relaunch Unity, or better yet, right click on the assets in the editor and select Reimport)
    This saves having to import the function.

    It should be possible to do this as well:
    Code (csharp):
    1. System.Windows.Forms.Cursor.Clip = new System.Drawing.Rectangle( x, y, width, height );
    but again this doesn't do a thing.
    It seems Unity's engine overrides the clipping somewhere, breaking my hopes and dreams in the process. Thanks guys, much appreciated :twisted:
     
  16. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    Even if I run the code in LateUpdate() ?
     
  17. taoa

    taoa

    Joined:
    Dec 10, 2009
    Posts:
    88
    Errm, well, setting the position of the cursor in LateUpdate instead of Update would make things being done even later wouldn't it?
    That's not going to help, no :)

    To do things that way, we'd need a way to set the cursor position before the operating system has a chance to change it itself. This is neither possible nor wise.

    The proper way would be to make use of Windows' functionality of clipping the cursor area, but.... that doesn't work! :cry: :p
     
  18. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    I probably didn't get the idea of how it works, then. :)
     
  19. taoa

    taoa

    Joined:
    Dec 10, 2009
    Posts:
    88
    Quite simple, really! :)

    This is what happens during a single frame:

    1) The user moves his mouse.
    2) The Operating system (here Windows) immediately picks up the movement and changes the cursor position accordingly, before any running program gets executed/looped (so this includes Unity).
    At this point, the cursor is positioned to where the user moved it, and this can be anywhere in the screens
    3) After that, Unity eventually gets looped over, and it will go over and update all the scripts it's been given, including the one where the cursor's position gets set to wherever is needed (here, inside the game frame).
    Now, the cursor has now been moved to inside the game frame.

    The problem is the time that passes between step 2 and 3. During this time, the user can click while the cursor is still outside the game window, loosing focus to whatever other application he has clicked on, and perhaps even getting out of full screen if he was in it.

    This problem goes beyond simply having the scripts being run too late. When you set
    Code (csharp):
    1. Screen.lockCursor = true
    you can still see the cursor in the editor. And if you move your mouse, you'll notice that the cursor moves, but gets positioned back to the centred position.
    This means that even the engine part of Unity has issues dealing with this. Potentially, if the mouse movement is large enough, the cursor could leave the game frame, even with lockCursor = true.

    The only proper solution to this is to clamp the system's cursor to a certain area, using either

    Code (csharp):
    1. [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )]
    2. public static extern bool ClipCursor( ref RECT rcClip );
    or

    Code (csharp):
    1. System.Windows.Forms.Cursor.Clip = new System.Drawing.Rectangle( x, y, width, height );
    because then it would be handled by the system itself.
    It appears this cannot be done on the scripts side.


    Save me Unity deities!
     
  20. taoa

    taoa

    Joined:
    Dec 10, 2009
    Posts:
    88
    Well, when it comes to Unity deities, it seems it's "Ask and Thou Shalt receive".

    I got it working, using ClipCursor.

    Do something like this:

    Code (csharp):
    1. [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )]
    2. [return: MarshalAs( UnmanagedType.Bool )]
    3. public static extern bool ClipCursor( ref RECT rcClip );
    4. [DllImport( "user32.dll" )]
    5. [return: MarshalAs( UnmanagedType.Bool )]
    6. public static extern bool GetClipCursor( out RECT rcClip );
    7. [DllImport( "user32.dll" )]
    8. static extern int GetForegroundWindow( );
    9. [DllImport("user32.dll")]
    10. [return: MarshalAs( UnmanagedType.Bool )]
    11. static extern bool GetWindowRect( int hWnd, ref RECT lpRect );
    12.  
    13. [StructLayout( LayoutKind.Sequential )]
    14. public struct RECT
    15. {
    16.     public int Left;
    17.     public int Top;
    18.     public int Right;
    19.     public int Bottom;
    20.     public RECT( int left, int top, int right, int bottom )
    21.     {
    22.         Left = left;
    23.         Top = top;
    24.         Right = right;
    25.         Bottom = bottom;
    26.     }
    27. }
    28.  
    29. RECT currentClippingRect;
    30. RECT originalClippingRect = new RECT( );
    31.  
    32. void Start()
    33. {
    34.     hndl = GetForegroundWindow( );
    35.     GetWindowRect( hndl, ref currentClippingRect );
    36.     GetClipCursor( out originalClippingRect );
    37.     ClipCursor( ref currentClippingRect);
    38. }
    39.  
    40. void OnApplicationQuit()
    41. {
    42.     ClipCursor( ref originalClippingRect );
    43. }
    and you should be golden.
    I don't get why it didn't work the first time, but now it does.

    This code will only work on a Windows standalone build. In the windows editor, the clipping rectangle will be the entire editor window, so keep that in mind.

    Oh, and don't forget to add "user32.dll" into your resources directory.

    Voilà!
    Keep this one in a corner of you head, it WILL come useful ^_^
     
    animatria likes this.
  21. ConjuredMuffin

    ConjuredMuffin

    Joined:
    Jun 4, 2011
    Posts:
    1
    Hey,
    I just found this thread and tried implementing this last method posted here.
    I'm getting a parsing error at this line though
    public static extern bool ClipCursor( ref RECT rcClip );

    Is there something in use here that is not available in the free version of unity or has something changed in unity 3.3?
    Also I'm using java script normally, so I don't get a lot of this c# stuff.

    I'm sorry if this is a stupid question, but I just got started and I would really love to resolve this issue since the mouse leaving the main screen really messes with my game controls...
     
  22. Cargh

    Cargh

    Joined:
    May 17, 2011
    Posts:
    36
    Why exactly was this functionality not implemented by default into Unity?

    Makes it impossible to play in windowed games, web player and full screen with multiple monitors.
     
  23. Undead

    Undead

    Joined:
    Nov 13, 2009
    Posts:
    49
    That was 4 1/2 years ago now, any chance this will ever get done? :)
     
  24. boban

    boban

    Joined:
    Aug 6, 2011
    Posts:
    11
  25. Angrycrow

    Angrycrow

    Joined:
    Jan 29, 2011
    Posts:
    34
    Wow. And still didn't take it..

    I guess people are happy replacing cursors and doing the dirty work themselves. I'm going to go ahead and allow the player to click outside the game on this 'feature.'
     
  26. v_m

    v_m

    Joined:
    Oct 2, 2011
    Posts:
    9
    what is this, C#? can someone pls give an example of how this should be included in a complete, working script? I don't understand C# and this above script is giving parsing errors. I presume there are things missing from it, that the author considered common knowledge (for C# people maybe :D ).
    thanks
     
  27. v_m

    v_m

    Joined:
    Oct 2, 2011
    Posts:
    9
    this can't possibly be a todo.. list.. thing for Unity... (?) I think this is a major issue, you can't ship a FPS type game for example without constraining the cursor inside the game... and so goes for probably most games you'd want to make with Unity.
     
  28. Undead

    Undead

    Joined:
    Nov 13, 2009
    Posts:
    49
    Finally got this to work on windows standalone....

    I would also STILL say this needs to be added to unity as having an engine that allows the cursor to escape the game window is just daft...:confused:

    Anyway for those who were also stuck like me, save this code as ClipCursorScript.cs and you should be fine..
    Ofc remember to add it to some game object so it gets called ;)

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Runtime.InteropServices;
    5.  
    6. public class ClipCursorScript : MonoBehaviour
    7. {
    8.     [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )]
    9.     [return: MarshalAs( UnmanagedType.Bool )]
    10.     public static extern bool ClipCursor( ref RECT rcClip );
    11.     [DllImport( "user32.dll" )]
    12.     [return: MarshalAs( UnmanagedType.Bool )]
    13.     public static extern bool GetClipCursor( out RECT rcClip );
    14.     [DllImport( "user32.dll" )]
    15.     static extern int GetForegroundWindow( );
    16.     [DllImport("user32.dll")]
    17.     [return: MarshalAs( UnmanagedType.Bool )]
    18.     static extern bool GetWindowRect( int hWnd, ref RECT lpRect );
    19.      
    20.     [StructLayout( LayoutKind.Sequential )]
    21.    
    22.     public struct RECT
    23.     {
    24.         public int Left;
    25.         public int Top;
    26.         public int Right;
    27.         public int Bottom;
    28.         public RECT( int left, int top, int right, int bottom )
    29.         {
    30.             Left = left;
    31.             Top = top;
    32.             Right = right;
    33.             Bottom = bottom;
    34.         }
    35.     }
    36.      
    37.     RECT currentClippingRect;
    38.     RECT originalClippingRect = new RECT( );
    39.      
    40.     void Start()
    41.     {
    42.         var hndl = GetForegroundWindow( );
    43.         GetWindowRect( hndl, ref currentClippingRect );
    44.         GetClipCursor( out originalClippingRect );
    45.         ClipCursor( ref currentClippingRect);
    46.     }
    47.      
    48.     void OnApplicationQuit()
    49.     {
    50.         ClipCursor( ref originalClippingRect );
    51.     }
    52. }
    53.  
     
  29. orionburcham

    orionburcham

    Joined:
    Jan 31, 2010
    Posts:
    491
    Undead, thanks a ton for posting your successful setup. I'm trying to get it working on my end but, with no luck so far. Before I commence a hair pulling dissection of what's going on, I want to cover the basics in case it's something simple:

    - I've got your script copied exactly and on a GO in my first scene.
    - I've also got user32.dll copied from Windows/System32/ into my project's root Resources folder
    - I'm working and compiling this from my mac, testing it on another windows PC.

    I get a DLL not found error:

    DllNotFoundException: user32.dll
    ClipCursorScript.Start () (at Assets/Scripts/MonoBehaviors/ClipCursorScript.cs:41)

    see any general problems with my setup?

    Thanks!
     
  30. Undead

    Undead

    Joined:
    Nov 13, 2009
    Posts:
    49
    I'm not sure to be honest, i don't have the .dll in the resources folder or the plugins folder as some suggest as it's a .dll that any windoze program should be able to access, maybe it's having issues with being on a mac, i'm using and compiling this on a windows 7 machine, maybe try adding the .dll to a plugins folder in your project if you're using unity pro?

    I'm still very much learning the engine myself and I use JS rather than C# so maybe others can suggest some reasons for why you get a missing .dll problem, my guess would be put the .dll in a plugins folder.
     
  31. RElam

    RElam

    Joined:
    Nov 16, 2009
    Posts:
    375
    Ok, I'm quite disappointed to find this thread. This is an absolutely game breaking bug for RPGs and many bird's eye view games for those using dual monitors. This is also not a small segment of the population, as I've seen polls indicating 1/3rd of users have a dual monitor setup (Virtually all my PC gamer friends do), and why not, even the cheapest video cards support it, and monitors get upgraded all the time. If you factor in that multi-monitor users are also more likely to spend money on games, since those who spend more money on rigs are more likely to spend money on games, you could be looking at nearly 50% of your paying customers these days.

    It's sad to see that this has been on the todo list for nearly 6 years, the bang for the buck on fixing this bug is staggering compared to some of the new features added lately. I like new features as much as the next guy, but not when there's stuff like this waiting around to jump out and screw me :(.

    This seems impossible to circumvent too, can't set mouse position to constrain manually (which likely wouldn't work anyways, but would be a nice feature for other things). Can't use a custom rendered cursor even if you were willing to accept the lag, since you seemingly can't simulate GUI events or move cursor, rebuilding the GUI system is a heck of a task. As much as I like working with the engine hitting stuff like this bothers me deeply.
     
  32. Tekniko

    Tekniko

    Joined:
    Jun 15, 2012
    Posts:
    50
    Could you not get the position of the cursor and if that is greater then or less then x,y resolution, then set cursor position to x/2, y/2 ??