在win10上,如果安装了某些输入法(比如QQ输入法),会造成unity的键盘事件被输入法捕获而不能触发的情况。只有将输入法切换到英文状态下才能响应键盘事件。
解决办法有,
1:用户主动切换输入法,甚至卸载输入法
2:程序在非输入状态下,屏蔽输入法
由于方法1在全屏状态下,用户完全不知道是否在输入法劫持中,常常导致以为是程序的bug,所以这里采用方法2
在unity中,官方并没有提供一个很好的解决方案(Input.imeCompositionMode无效)。所以只能借助win api。
其中最为重要的API是设置输入法状态:
[DllImport("imm32.dll")]
private static extern bool ImmSetOpenStatus(IntPtr himc, bool b);
其中himc为当前正在输入的窗口的输入法句柄,b为true表示开启,false表示关闭
himc可以通过另外一个api函数获取
[DllImport("imm32.dll")]
private static extern IntPtr ImmGetContext(IntPtr hwnd);
其中,hwnd为程序窗口的句柄
该值的获取方式可以参考:http://blog.csdn.net/linkrules/article/details/50420797
整个代码如下:
using System; using System.Diagnostics; using System.Runtime.InteropServices; public class Win32Help { private delegate bool Wndenumproc(IntPtr hwnd, uint lParam); [DllImport("user32.dll", SetLastError = true)] private static extern bool EnumWindows(Wndenumproc lpEnumFunc, uint lParam); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetParent(IntPtr hWnd); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, ref uint lpdwProcessId); [DllImport("kernel32.dll")] private static extern void SetLastError(uint dwErrCode); /// <summary> /// 获取当前进程的窗口句柄 /// </summary> /// <returns></returns> public static IntPtr GetProcessWnd() { var ptrWnd = IntPtr.Zero; var pid = (uint)Process.GetCurrentProcess().Id; // 当前进程 ID var bResult = EnumWindows(delegate (IntPtr hwnd, uint lParam) { uint id = 0; if (GetParent(hwnd) != IntPtr.Zero) return true; GetWindowThreadProcessId(hwnd, ref id); if (id != lParam) return true; ptrWnd = hwnd; // 把句柄缓存起来 SetLastError(0); // 设置无错误 return false; // 返回 false 以终止枚举窗口 }, pid); return (!bResult && Marshal.GetLastWin32Error() == 0) ? ptrWnd : IntPtr.Zero; } [DllImport("imm32.dll")] private static extern IntPtr ImmGetContext(IntPtr hwnd); [DllImport("imm32.dll")] private static extern bool ImmGetOpenStatus(IntPtr himc); [DllImport("imm32.dll")] private static extern bool ImmSetOpenStatus(IntPtr himc, bool b); /// <summary> /// 设置输入法状态 /// </summary> /// <param name="tf"></param> public static void SetImeEnable(bool tf) { var handle = GetProcessWnd(); var hIme = ImmGetContext(handle); ImmSetOpenStatus(hIme, tf); } /// <summary> /// 获取输入法状态 /// </summary> /// <returns></returns> public bool GetImeStatus() { var handle = GetProcessWnd(); var hIme = ImmGetContext(handle); return ImmGetOpenStatus(hIme); } }
然后在程序中可以使用Win32Help.SetImeEnable(false)的方法来屏蔽输入法,比如:
using UnityEngine;
public class InputTest : MonoBehaviour { // Use this for initialization protected void Start() { Win32Help.SetImeEnable(false); } // Update is called once per frame protected void Update() { if (Input.GetKey(KeyCode.A)) { Debug.Log("AAAAAAAAAAAs"); } } }
注意:只对Windows系统有效,Win10测试可用,其他系统未经测试