如何在使用Unity新输入系统时,将光标锁定时防止“鼠标移动”事件?

huangapple go评论83阅读模式
英文:

How to prevent "mouse move" event when Cursor is set to locked using Unity new input system?

问题

我正在为一款游戏添加控制器和键鼠支持。我希望它能按照以下方式工作:

如果用户按下控制器上的任何按钮,UI 将响应并显示与控制器相关的输入图标,同时隐藏鼠标光标。

如果用户移动鼠标或按下键盘上的键,UI 将响应并显示与键鼠相关的输入图标,并显示鼠标光标。

我解决这个问题的方法是简单地调用一个方法,根据最近使用的设备适当地通知UI,并根据情况隐藏或显示鼠标。这个方法在每次输入时都会被调用,但如果输入没有从上次调用发生变化,它实际上不会执行任何操作。

我遇到的问题是隐藏鼠标。为了响应鼠标移动,我设置了一个监听器如下:

protected void OnEnable()
{
    GameInput.UI.Point.performed += OnMouseMove;
    GameInput.UI.Point.Enable();
}

private void OnMouseMove() 
{
    // 通知UI切换到键鼠输入
    UnityEngine.Cursor.lockState = CursorLockMode.None; // 解锁光标
    UnityEngine.Cursor.visible = true; // 显示光标
}

每当输入设备切换到游戏手柄时,会运行以下代码:

private void OnInputDeviceChangedToGamePad() 
{
    // 通知UI切换到游戏手柄输入
    UnityEngine.Cursor.lockState = CursorLockMode.Locked; // 这是我的问题所在
}

当锁定光标状态设置为锁定时,Unity 会奇怪地触发 GameInput.UI.Point.performed。这导致 OnMouseMove 方法运行,将当前输入设备设置为键鼠并解锁光标。

我尝试了一些不同的方法来解决这个问题:

  • 重新排列代码的顺序,希望在鼠标事件触发后,切换到游戏手柄的操作发生。这不起作用,因为一旦光标被锁定,鼠标事件就会触发并显示光标。
  • 使用协程以及一个布尔值来延迟时间,并且只有在布尔值正确设置时才解锁光标。

第二种方法有效,但不够稳定,而且其他代码的更改使其变得不太可靠。而且,这有点像一个破解,因为输入设备的设置被延迟了,因此可能会运行依赖于正确设置的代码。

我不知所措。如何在不调用 InputAction 的 .performed 阶段的情况下设置光标的锁定状态?还有其他可能我遗漏的方法吗?

英文:

I am adding controller and KBM support to a game. I would like for this to work as follows:

If user presses any button on the controller, the UI responds by showing controller related input icons and hiding the mouse cursor.

If user moves the mouse or presses a key, the UI responds by showing KBM related input icons and displaying the mouse cursor.

My solution for this is to simply call a method that appropriately informs the UI of the most recent device used and hides or shows the mouse based on that. This method gets called on every input, but it doesn't really do anything if the input didn't change from the last call.

The problem I am having is on hiding the mouse. To respond to mouse movement I setup a listener for it as such:

protected void OnEnable()
{
    GameInput.UI.Point.performed += OnMouseMove;
    GameInput.UI.Point.Enable();
}

private void OnMouseMove() 
{
    //inform UI of change to KBM
    UnityEngine.Cursor.lockState = CursorLockMode.None; //unlock cursor
    UnityEngine.Cursor.visible = true; //show cursor
}

Whenever the input device changes to the gamepad, the following code is ran:

private void OnInputDeviceChangedToGamePad() 
{
    //inform UI of change to GamePad
    UnityEngine.Cursor.lockState = CursorLockMode.Locked; //here is where my problem begins
}

When the lockstate is set to locked Unity oddly fires GameInput.UI.Point.performed. This causes the OnMouseMove method to run, which sets the current input device to KBM and unlocks the cursor.

I have tried a few different approaches to fix this:

  • Rearranging the order of the code in hopes that while the mouse event fires, the change to gamepad occurs after. This doesn't work because once the cursor is locked, mouse is fired and unhidden.
  • Using coroutines along with a bool to delay timing and only unlock cursor if the bool is set properly.

The second one works, but it isn't consistent, and other code changes have rendered it quite unreliable. Also, it's a bit of hack that messes things up because the setting of the input device is delayed, and so code can run that might depend on having it set properly.

I'm at a loss here. How can I set the lockstate of the cursor without calling the .performed phase of the InputAction? Is there another approach to this that perhaps I'm missing?

答案1

得分: 1

根据@BugFinder的建议,解决这个问题的方法是在锁定鼠标之前简单地取消注册鼠标的操作,然后在锁定鼠标后重新注册。此外,一个好的方法是在KBM模式下保持事件未注册。

然而,这里有一个小问题:锁定鼠标不会立即完成其移动,而是在当前帧内完成。因此,您至少需要使用yield return null;来重新注册之前。我选择添加一个小的时间延迟,而不仅仅是一个帧,以防止用户在KBM和游戏手柄之间快速切换。

我的解决方案如下:

private void OnInputDeviceChangedToGamePad() 
{
    GameInput.UI.Point.performed -= OnMouseMove;
    // 通知UI切换到GamePad
    UnityEngine.Cursor.lockState = CursorLockMode.Locked;
    StartCoroutine(DelayMouseMoveRegistration());
}

private IEnumerator DelayMouseMoveRegistration() 
{
    yield return new WaitForSecondsRealtime(0.5f);
    GameInput.UI.Point.performed += OnMouseMove;
}

希望这对您有所帮助。

英文:

As suggested by @BugFinder, the solution to this problem is to simply unregister the Mouse's performed action from OnMouseMove prior to locking the mouse, and then re-register afterwards. Additionally, a good approach would be to leave the event unregistered while in KBM mode.

However, there is on slight issue with this: locking the mouse does not complete its movement instantly, it does so within the current frame. Thus, you would need to at least use yield return null; before registering again. I opted to add a small time delay instead of just the frame, to prevent the user from rapidly switching back and forth between KBM and gamepad.

My solution looks like this:

private void OnInputDeviceChangedToGamePad() 
{
    GameInput.UI.Point.performed -= OnMouseMove;
    //inform UI of change to GamePad
    UnityEngine.Cursor.lockState = CursorLockMode.Locked;
    StartCoroutine(DelayMouseMoveRegistration());
}

private IEnumerator DelayMouseMoveRegistration() 
{
    yield return new WaitForSecondsRealtime(0.5f);
    GameInput.UI.Point.performed += OnMouseMove;
}

huangapple
  • 本文由 发表于 2023年6月22日 12:39:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76528645.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定