Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

CursorLockMode.Confined is doing it wrong

Discussion in 'Scripting' started by Twitwi, Aug 20, 2015.

  1. Twitwi

    Twitwi

    Joined:
    Apr 11, 2012
    Posts:
    22
    I am making a strategy game and I have to confine the cursor to the window (in both fullscreen and windowed mode)

    So of course you would run Cursor.lockState = CursorLockMode.Confined;
    But, (I am guessing) underneath on windows it will run GetWindowRect and then call ClipCursor on that rect. This results in the wrong behaviour.

    See the mouse is not constricted from touching the window controls (i.e. minimize, maximize and close window)


    So I am doing it myself now, but every time the Cursor.lockState is changed this resets and have to be reapplied when I want to confine the cursor again. I see no reason for CursorLockMode.Confined to work like that, other than it is the easiest to implement.

    Here is some of the code in case you are having the same issue.
    Code (CSharp):
    1. [...]
    2.  
    3. public static WindowRect GetPlayRect(IntPtr hWnd)
    4. {
    5.     WindowRect playRect = new WindowRect();
    6.     Point pos = new Point(0, 0);
    7.  
    8.     GetClientRect(hWnd, ref playRect);
    9.     ClientToScreen(hWnd, ref pos);
    10.  
    11.     playRect.Left += pos.X;
    12.     playRect.Right += pos.X;
    13.     playRect.Top += pos.Y;
    14.     playRect.Bottom += pos.Y;
    15.  
    16.     return playRect;
    17. }
    18.  
    19. [...]
     
    herb_nice likes this.
  2. Twitwi

    Twitwi

    Joined:
    Apr 11, 2012
    Posts:
    22
    Now some .gifs
    Here is how Unity is doing it:


    Here is how Unity should be doing it:
     
    herb_nice, nco2k and MattJWPerkins like this.
  3. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    Why would you want to stop the user from using the window controls? Thats not good from a user perspective. Especially if the game is windowed. What happens if I want to minimize your game to, for example, open my web browser?
     
  4. jason_estey

    jason_estey

    Joined:
    Jun 2, 2015
    Posts:
    1
    What Twitwi is asking for seems reasonable to me for an RTS game. When people are playing StarCraft 2, for instance, they don't want to be accidentally sending an application kill message in the middle of their match because they were frantically clicking near the corner of the screen. It's also not very different from other game types where the mouse cursor is hidden entirely; the player won't be able to click the window bar in that case either. The player can always get out of the app by using alt-tab.
     
    Dieter_Dagger likes this.
  5. Korno

    Korno

    Joined:
    Oct 26, 2014
    Posts:
    518
    True, if you were playing full screen, but windowed applications (especially when they are displaying the minimize button) imply that you can multitask and minimize the application. I would be annoyed if the application prevented that, but hey this is just a personal opinion.

    Of course there is Alt-Tab.

    You should try Starcraft 2 in windowed mode by the way.
     
  6. Twitwi

    Twitwi

    Joined:
    Apr 11, 2012
    Posts:
    22
    Sure Star Craft does not confine the cursor, but I would disagree with that being a good thing:



    But in any case if I wanted it to work like star craft I would just be using CursorLockMode.None and be fine.
    I don't believe there is a single use case for confining the cursor to the window area and not the client area. I believe the current implementation is wrong, and it is an oversight from Unity's part.
     
  7. Kotomine_Kirei

    Kotomine_Kirei

    Joined:
    Jul 27, 2016
    Posts:
    2
    Any solution to this as of yet? I'm currently having the same issues as OP and the Confined setting is not cutting it.
     
  8. Twitwi

    Twitwi

    Joined:
    Apr 11, 2012
    Posts:
    22
    For what it is worth, the workaround code in the OP works.

    It just gets annoying when you work with PlayMaker or uScript or any other third party library that touches the CursorLockedMode.

    Also the workaround is specifically to Windows. I cannot remember if CursorLockedMode.Confined is also broken on Mac and Linux too.
     
  9. Lisk

    Lisk

    Joined:
    Oct 23, 2013
    Posts:
    99
    Would you mind posting the full code for the workaround? I'm having trouble figuring out how to use WindowRect (can't find it).
     
  10. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,574
    If you think the functionality is broken, it would be from advantage to submit a bug-report following the advice in this document. That will eventually allow to get rid of workarounds as well.
     
  11. Twitwi

    Twitwi

    Joined:
    Apr 11, 2012
    Posts:
    22
    It's a few years ago now, but I believed I did open a bug back then. The description of the issue was a link to this thread. I cannot find it anymore, maybe I submitted it wrong or something ¯\_(ツ)_/¯. In any case, this thread was made to bring attention to the bug, in hopes that we wouldn't need the workaround anymore.

    Back in 2015 I was working at Blackbird Interactive. Unfortunately, I do not work there anymore, so I don't have the source code. (maybe @yoyobbi / @yoyo might help you out)

    To do it, you need to make some native calls to windows.
    The WindowRect (https://msdn.microsoft.com/en-us/library/windows/desktop/dd162897(v=vs.85).aspx) is struct with 4 int/longs: left,top, right, bottom;
    GetClientRect: https://msdn.microsoft.com/en-us/library/windows/desktop/ms633503(v=vs.85).aspx
    ClientToScreen: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183434(v=vs.85).aspx

    Here is a guide to call native functions: https://msdn.microsoft.com/en-us/library/ms235282.aspxsd

    Note: I do not know if CursorLockMode.Confined works properly on the other platforms, but this fix will only work for Windows.
     
  12. Tehelee

    Tehelee

    Joined:
    Jan 26, 2013
    Posts:
    12
    Sorry to resurrect this old thread, but I had a friend ask me to help him out with this.
    I took @Twitwi 's example and constructed a couple of scripts to handle the binding with the native libraries (Binding.cs) and the MonoBehaviour (Bounding.cs) to demonstrate the functionality. There's a slight delay that I can't pin down, it could have been my setup though.

    You'll need to pull your user32.dll libraries from C:\Windows\System32\ and C:\Windows\SysWOW64\ for x86 and x86_64 architecture respectively.
    You'll also need the System.Drawing.dll from C:\Program Files\Unity\Editor\Data\Mono\lib\mono\2.0\

    These would go in the following hierarchy somewhere in your assets folder:

    Be sure to set these DLLs to Windows only, and set the user32.dll to their respective CPU architectures.
    (You'll find these in the dll's inspector.)

    If you're feeling bold or know what you're doing you could extract necessary the classes from System.Drawing and hard-code them in your own project to eliminate bloat.
     

    Attached Files:

  13. Twitwi

    Twitwi

    Joined:
    Apr 11, 2012
    Posts:
    22
    Don't be sorry @Tehelee ! :D
    I intend for this thread to be alive until the day it is fixed :p
    I was sure that I've submitted it as a bug back in the day, but I have been unable to find the report. That said, someone else has stumbled upon this back in 2017 it seems: https://issuetracker.unity3d.com/is...o-out-of-the-top-side-of-a-game-window-bounds (so I hope they are aware now)
    Anyhow, the reason a fix would be nice despite the workaround is that of plugins using Cursor.lockState (like the visual programming ones, uScript, bolt). They, as far as I can remember, change Cursor.lockState, which sucks, because if a designer uses that (what could possibly be wrong with the defaults ...) then that breaks this.

    Anyways, some critique of your sample (hope it is helpful for the populi) but I have a couple of suggestions.
    1. Don't distribute user32.dll, it is a system library, you can be pretty sure that the end-user will have it, as Windows cannot really run without it. (spare yourself the maintenance of keeping that DLL up-to-date)
    2. Be bold write the Point class (this is the only reason you need System.Drawing anyway) The point class is 2 ints: X & Y. That way you don't have to distribute System.Drawing either!
    3. Set Cursor.lockState anyway (but overwrite its behavior) In case you have some scripts that check if the cursor is locked or not, this is also a little forward thinking, as if unity does fix this one day, all you need to do is remove your clipping code. (imagine if you've built a system to keep track of your confine cursor for this case)

    I also think you went a little overboard with your window rect (if it is only used here/for this) but ¯\_(ツ)_/¯ that's more a matter of taste

    Here is the point (and yes, that is really all you're needing the System.Drawing.dll for):
    Code (CSharp):
    1. [StructLayout(LayoutKind.Sequential)]
    2. private struct Point
    3. {
    4.     public int X;
    5.     public int Y;
    6. }
     
    herb_nice likes this.
  14. malkere

    malkere

    Joined:
    Dec 6, 2013
    Posts:
    1,212
    So many problems with this in 2018.2.2 =[
    After losing focus once cursorLock breaks completely and you can no longer use confined nor locked. Even worse, on Linux when the mouse goes off window it stops sending input and you can't rotate first/third person cameras.
     
    herb_nice and nco2k like this.