Unity3D Mouse Position

Hallo!

I guess a lot of you will use Unity3D, as it is a nice tool and making an FPS with it is pretty easy. I would use it, too, if I would have time to participate. Nonetheless, here is a little gem of wisdom I’d like to share: Unity works in Linux via Winebut not perfectly. If you don’t need mouse movement, I guess you’re fine, but as soon as you want that funky look around camera control via mouse you love in your modern FPSes, the bad news is that under Wine the following code (C#) doesn’t do what you expect:

transform.RotateAround(Vector3.up, Input.GetAxis("Mouse X") * horizontalSpeed * sensitivity);

It won’t rotate, as Input.GetAxis(“Mouse X”) (and “Mouse Y”, too) will always return 0. Okay, you say, perhaps I just want to use Input.mousePosition and calculate the delta between the mouse position from last frame to this frame? Well, that works, but only if you don’t useScreen.lockCursor = true – which unfortunately is the only way in Unity to reposition the cursor (it transfers it to the center of the game window every frame). You could also useScreen.showCursor = false, but of course it has the problem that the cursor sooner or later will drop out of the window or hit the screen’s borders.

So, what is the solution here? The one I found lies in this forum thread (Unity3D support forum) – but if you know a better and simpler one, please share! Anyway, this is what’s going on:

  • Get the mouse position via the user32.dll (so be careful to only use this code for the Windows version – use #if UNITY_STANDALONE_WIN for that).
  • Get the delta mouse movement by subtracting the last mouse position from the screen center.
  • Reposition the cursor to the screen  center via user32.dll.
  • Hide the mouse cursor via Screen.showCursor = false.

One little problem with this approach is the fact that I didn’t find a way to get the exact center of the game window in the Unity editor (because you can move it around), so sometimes the “center” is outside of this window, but I guess you can circumvent that by using the normalInput.GetAxis(“Mouse X”) in the editor. Nonetheless, here is the code I use:

First, the WindowsCursorHandling.cs (most parts blindly copied from the aforementioned thread) – you have to add that component to a gameobject in the scene.

using UnityEngine;

#if UNITY_STANDALONE_WIN
using System.Collections;
using System.Runtime.InteropServices;

public class WindowsCursorHandling : MonoBehaviour
{
 [DllImport( "user32.dll", CharSet = CharSet.Auto, ExactSpelling = true )]
 [return: MarshalAs( UnmanagedType.Bool )]
 public static extern bool ClipCursor( ref RECT rcClip );
 [DllImport( "user32.dll" )]
 [return: MarshalAs( UnmanagedType.Bool )]
 public static extern bool GetClipCursor( out RECT rcClip );
 [DllImport( "user32.dll" )]
 static extern int GetForegroundWindow( );
 [DllImport("user32.dll")]
 [return: MarshalAs( UnmanagedType.Bool )]
 static extern bool GetWindowRect( int hWnd, ref RECT lpRect );
 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 public static extern int SetCursorPos(int x, int y);
 [DllImport("user32.dll")]
 [return: MarshalAs( UnmanagedType.Bool )]
 public static extern bool GetCursorPos(out POINT point);

 private static RECT currentClippingRect;
 private static RECT originalClippingRect = new RECT( );
 private static bool isClipped = false;

 public static int centerX;
 public static int centerY;

 [StructLayout( LayoutKind.Sequential )]
 public struct RECT
 {
  public int Left;
  public int Top;
  public int Right;
  public int Bottom;
  public RECT( int left, int top, int right, int bottom )
  {
   Left = left;
   Top = top;
   Right = right;
   Bottom = bottom;
  }
 }

 [StructLayout( LayoutKind.Sequential )]
 public struct POINT
 {
  public int x;
  public int y;
  public POINT( int X, int Y )
  {
   x = X;
   y = Y;
  }
 }

 void Start()
 {
  GetClipCursor( out originalClippingRect );
  centerX = originalClippingRect.Left + (originalClippingRect.Right - originalClippingRect.Left) / 2;
  centerY = originalClippingRect.Top + (originalClippingRect.Bottom - originalClippingRect.Top) / 2;
 }

 public static void StartClipping()
 {
  if (isClipped)
   return;

  var hndl = GetForegroundWindow( );
  GetWindowRect( hndl, ref currentClippingRect );
  ClipCursor( ref currentClippingRect);

#if UNITY_EDITOR
  centerX = Screen.width / 2;
  centerY = Screen.height / 2;
#else
  centerX = currentClippingRect.Left + (currentClippingRect.Right - currentClippingRect.Left) / 2;
  centerY = currentClippingRect.Top + (currentClippingRect.Bottom - currentClippingRect.Top) / 2;
#endif
  isClipped = true;
 }

 static public void CenterCursor()
 {
  SetCursorPos(centerX, centerY);
 }

 void OnApplicationQuit()
 {
  StopClipping();
 }
 public static void StopClipping()
 {
  isClipped = false;
  ClipCursor(ref originalClippingRect);
 }
}
#else
public class WindowsCursorHandling : MonoBehaviour
{
}
#endif

And this is how it then looks in my Camera Controller for FPS mouse movement:

#if UNITY_STANDALONE_WIN
 WindowsCursorHandling.POINT p;
 WindowsCursorHandling.GetCursorPos(out p);
 float factorX = 40.0f * (p.x - WindowsCursorHandling.centerX) / (float)Screen.width;
 float factorY = -40.0f * (p.y - WindowsCursorHandling.centerY) / (float)Screen.width;
 WindowsCursorHandling.CenterCursor();
 transform.RotateAround(Vector3.up, factorX * horizontalSpeed);
#else
 transform.RotateAround(Vector3.up, Input.GetAxis("Mouse X") * horizontalSpeed);
#endif
// do similar stuff with "Mouse Y" movement

The magic number 40.0f was found by trial-and-error, so I don’t know if it’s really the best value for every PC configuration. You still need the cursor hiding stuff somewhere, and of course callWindowsCursorHandling.StartClipping() at initialization. Whenever I wanted to just call Screen.lockCursor I now call this function instead:

static public void SetCursorHidden(bool hide)
{
#if UNITY_STANDALONE_WIN
 Screen.showCursor = !hide;
 if (hide)
  WindowsCursorHandling.StartClipping();
 else
  WindowsCursorHandling.StopClipping();
#else
 Screen.lockCursor = hide;
#endif
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值