在Unity中,当Unity没有输入焦点时捕获按键按下事件

7

我需要Unity捕获所有按键,即使Unity没有焦点。

我尝试使用:

Input.KeyPress()

但是这似乎只能在Unity获得用户输入的焦点时才起作用。我需要它在没有焦点时也能工作,例如当我正在查看/使用其他Windows程序时。
PS:我已经在播放器首选项中打开了“后台运行”选项。
2个回答

12

完全有可能!但是,仅使用Unity3D内置的工具是无法完成的。您需要使用本地库来实现。

以下示例使用具有WH_KEYBOARD钩子类型的钩子链,该钩子类型对应于消息级键盘钩子。您可以在这里阅读有关SetWindowsHookEx和不同类型的更多信息[1]。

您可以检查挂接此类消息类型(WH_KEYBOARD)时收到的参数[2]。

using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;

public class KBHooks : MonoBehaviour
{
    [DllImport("user32")]
    protected static extern IntPtr SetWindowsHookEx(
        HookType code, HookProc func, IntPtr hInstance, int threadID);

    [DllImport("user32")]
    protected static extern int UnhookWindowsHookEx(
        IntPtr hhook);

    [DllImport("user32")]
    protected static extern int CallNextHookEx(
        IntPtr hhook, int code, IntPtr wParam, IntPtr lParam);

    // Hook types. To hook the keyboard we only need WH_KEYBOARD
    protected enum HookType : int
    {
        WH_JOURNALRECORD = 0,
        WH_JOURNALPLAYBACK = 1,
        WH_KEYBOARD = 2,
        WH_GETMESSAGE = 3,
        WH_CALLWNDPROC = 4,
        WH_CBT = 5,
        WH_SYSMSGFILTER = 6,
        WH_MOUSE = 7,
        WH_HARDWARE = 8,
        WH_DEBUG = 9,
        WH_SHELL = 10,
        WH_FOREGROUNDIDLE = 11,
        WH_CALLWNDPROCRET = 12,
        WH_KEYBOARD_LL = 13,
        WH_MOUSE_LL = 14
    }

    protected IntPtr m_hhook = IntPtr.Zero;
    protected HookType m_hookType = HookType.WH_KEYBOARD;

    protected delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

    //We install the hook and hold on to the hook handle.
    //The handle will be need to unhook. 
    protected bool Install(HookProc cbFunc)
    {
        if (m_hhook == IntPtr.Zero)
            m_hhook = SetWindowsHookEx(
                m_hookType, 
                cbFunc, 
                IntPtr.Zero, 
                (int)AppDomain.GetCurrentThreadId());

        if (m_hhook == IntPtr.Zero)
            return false;

        return true;
    }

    protected void Uninstall()
    {
        if (m_hhook != IntPtr.Zero)
        {
            UnhookWindowsHookEx(m_hhook);
            m_hhook = IntPtr.Zero;
        }
    }

    protected int CoreHookProc(int code, IntPtr wParam, IntPtr lParam)
    {
        if (code < 0)
            return CallNextHookEx(m_hhook, code, wParam, lParam);

        Debug.Log(
            "hook code =" + code.ToString() + 
            " lparam=" + lParam.ToString() + 
            " wparam=" + wParam.ToString());

        // Yield to the next hook in the chain
        return CallNextHookEx(m_hhook, code, wParam, lParam);
    }

    // Use this for initialization
    void Start()
    {
        Debug.Log("install hook");
        Install(CoreHookProc);
    }

    void OnDisable()
    {
        Debug.Log("Uninstall hook");
        Uninstall();
    }

}

这个例子来自[这篇博客][3]。

这种挂钩的方法适用于Windows系统。如果您需要在OS X或Linux上创建单独的挂钩,您需要以该操作系统的本机方式进行。

我不能发布超过1个链接,因为我在SO上缺乏声望。 我希望其中一个管理员会相应地编辑我的帖子。

 [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
 [2]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
 [3]: http://phardera.blogspot.com.es/2010/12/windows-hooks-in-unity3d.html

8

我基于@boris-makogonyuk的答案制作了一个Unity包,并进行了一些可用性改进。

该软件包可在GitHub上获取(MIT许可证):https://github.com/Elringus/UnityRawInput

您可以按照以下方式使用它:

包含软件包名称空间。

using UnityRawInput;

初始化输入服务以开始处理本地输入消息。

RawKeyInput.Start();

可选的,您可以指定在应用程序失去焦点时是否处理输入消息(默认情况下禁用)。

var workInBackground = true;
RawKeyInput.Start(workInBackground);

为输入事件添加监听器。

RawKeyInput.OnKeyUp += HandleKeyUp;
RawKeyInput.OnKeyDown += HandleKeyDown;

private void HandleKeyUp (RawKey key) { ... }
private void HandleKeyDown (RawKey key) { ... }

您还可以检查当前是否按下了特定键。

if (RawKeyInput.IsKeyDown(key)) { ... }

你可以随时停止该服务。
RawKeyInput.Stop();

在不再需要监听器时,请不要忘记将其移除。

private void OnDisable ()
{
    RawKeyInput.OnKeyUp -= HandleKeyUp;
    RawKeyInput.OnKeyDown -= HandleKeyDown;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接