MonoGame 中的跨平台文本框输入 (Windows/Ubuntu)
Cross-platform textbox input in MonoGame (Windows/Ubuntu)
我正在开发一款使用 MonoGame 的桌面平台游戏。以前,它仅针对 Windows 并使用纯 XNA。我正在探索使用 MonoGame 使其支持其他平台的选项,我当前的备用测试平台是 Ubuntu 15.04.
该游戏的一项功能是支持文本框,其行为类似于标准 windows UI 文本框控件。为了实现这一点,我使用了 this Whosebug answer.
中的代码
我的问题是:我如何从链接的答案中编写功能的可移植版本,使其可以在备用桌面平台上运行(在这种情况下, Ubuntu)?我正在寻找能够在 Windows 和 Ubuntu.
上捕获游戏 window 按键事件的东西
附加信息:
链接的答案覆盖游戏 window 的 WndProc
,并通过触发事件(CharEntered
、KeyDown
、KeyUp
).我已经将答案的代码重构到可以将所有 Windows 功能封装在一个 "events" class:
中的程度
internal class Win32KeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
private readonly IntPtr _prevWndProc;
private readonly NativeMethods.WndProc _hookProcDelegate;
public Win32KeyboardEvents(GameWindow window)
{
_hookProcDelegate = HookProc;
_prevWndProc = (IntPtr)NativeMethods.SetWindowLong(window.Handle, NativeMethods.GWL_WNDPROC,
(int)Marshal.GetFunctionPointerForDelegate(_hookProcDelegate));
}
private IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = NativeMethods.CallWindowProc(_prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case NativeMethods.WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | NativeMethods.DLGC_WANTALLKEYS);
break;
case NativeMethods.WM_KEYDOWN:
if (KeyDown != null) //fire events that are subscribed to by textbox object based on which windows message is received
KeyDown(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_CHAR:
if (CharEntered != null)
CharEntered(null, new Win32CharEnteredEventArgs((char)wParam, lParam.ToInt32()));
break;
}
return returnCode;
}
}
我第一次尝试在 Ubuntu 上运行的东西是创建一个 GameComponent
派生的对象,该对象侦听组件 Update()
中的键盘输入并适当地触发事件,但是这很快就变得太复杂了。
我已经使用 GTK# 进行了调查,但有相当多的依赖项我不想安装,除非 GTK# 是最佳选择。这是我的 GTK# 尝试,由于缺少依赖项,目前尚未测试:
internal class CrossPlatformKeyboardEvents : DrawingArea, IKeyboardEvents
{
public event CharEnteredHandler CharEntered = delegate { };
public event KeyEventHandler KeyDown = delegate { };
public event KeyEventHandler KeyUp = delegate { };
public CrossPlatformKeyboardEvents()
{
Application.Init();
Application.Run();
AddEvents((int)EventMask.KeyPressMask);
AddEvents((int)EventMask.KeyReleaseMask);
}
[ConnectBefore]
protected override bool OnKeyPressEvent(EventKey evnt)
{
if (IsPastEvent(evnt))
{
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_PASTE_CODE));
return base.OnKeyPressEvent(evnt);
}
switch (evnt.Key)
{
case Key.Return:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_RETURNKEY_CODE));
break;
case Key.BackSpace:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_BACKSPACE_CODE));
break;
case Key.Tab:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_TAB_CODE));
break;
}
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyDown(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyPressEvent(evnt);
}
[ConnectBefore]
protected override bool OnKeyReleaseEvent(EventKey evnt)
{
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyUp(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyReleaseEvent(evnt);
}
private bool IsPastEvent(EventKey evnt)
{
return (evnt.State & ModifierType.ControlMask) > 0 && (evnt.Key == Key.V || evnt.Key == Key.v);
}
private Keys GetXNAKey(Key key)
{
if ((key >= Key.Key_0 && key <= Key.Key_9) ||
(key >= Key.A && key <= Key.Z))
{
return (Keys) key;
}
if (key >= Key.a && key <= Key.z)
{
return (Keys) (key - 32);
}
return Keys.None;
//switch (key)
//{
//}
}
我通过在 MonoGame 中使用 TextInput event on GameWindow 解决了这个问题。
这是一个扩展,它是 MonoGame 的一部分,但不是 Xna 的一部分。目前,我有 2 个项目——一个用于标准 Xna,一个用于 Monogame。 Monogame 项目链接到我的 Xna 项目中的所有文件,因此它们共享相同的代码。
由于我的项目设置方式,我还必须添加一个编译器标志 (MONO),以排除此代码针对标准 XNA 进行编译。
完整 class(替换问题中发布的 CrossPlatformKeyboardEvents):
internal sealed class MonoGameKeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
private readonly GameWindow _window;
public MonoGameKeyboardEvents(GameWindow window)
{
_window = window;
#if !MONO
if (CharEntered != null) //hide warning for "member is not used"
CharEntered(null, null);
#else
_window.TextInput += GameWindow_TextInput;
#endif
}
#if MONO
private void GameWindow_TextInput(object sender, TextInputEventArgs e)
{
if (CharEntered != null)
{
CharEntered(null, new CharEnteredEventArgs(e.Character));
}
}
~MonoGameKeyboardEvents()
{
Dispose(false);
}
#endif
public void Dispose()
{
#if !MONO
}
#else
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!disposing) return;
_window.TextInput -= GameWindow_TextInput;
}
#endif
}
我正在开发一款使用 MonoGame 的桌面平台游戏。以前,它仅针对 Windows 并使用纯 XNA。我正在探索使用 MonoGame 使其支持其他平台的选项,我当前的备用测试平台是 Ubuntu 15.04.
该游戏的一项功能是支持文本框,其行为类似于标准 windows UI 文本框控件。为了实现这一点,我使用了 this Whosebug answer.
中的代码我的问题是:我如何从链接的答案中编写功能的可移植版本,使其可以在备用桌面平台上运行(在这种情况下, Ubuntu)?我正在寻找能够在 Windows 和 Ubuntu.
上捕获游戏 window 按键事件的东西附加信息:
链接的答案覆盖游戏 window 的 WndProc
,并通过触发事件(CharEntered
、KeyDown
、KeyUp
).我已经将答案的代码重构到可以将所有 Windows 功能封装在一个 "events" class:
internal class Win32KeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
public event KeyEventHandler KeyDown;
public event KeyEventHandler KeyUp;
private readonly IntPtr _prevWndProc;
private readonly NativeMethods.WndProc _hookProcDelegate;
public Win32KeyboardEvents(GameWindow window)
{
_hookProcDelegate = HookProc;
_prevWndProc = (IntPtr)NativeMethods.SetWindowLong(window.Handle, NativeMethods.GWL_WNDPROC,
(int)Marshal.GetFunctionPointerForDelegate(_hookProcDelegate));
}
private IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = NativeMethods.CallWindowProc(_prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case NativeMethods.WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | NativeMethods.DLGC_WANTALLKEYS);
break;
case NativeMethods.WM_KEYDOWN:
if (KeyDown != null) //fire events that are subscribed to by textbox object based on which windows message is received
KeyDown(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new XNAKeyEventArgs((Keys)wParam));
break;
case NativeMethods.WM_CHAR:
if (CharEntered != null)
CharEntered(null, new Win32CharEnteredEventArgs((char)wParam, lParam.ToInt32()));
break;
}
return returnCode;
}
}
我第一次尝试在 Ubuntu 上运行的东西是创建一个 GameComponent
派生的对象,该对象侦听组件 Update()
中的键盘输入并适当地触发事件,但是这很快就变得太复杂了。
我已经使用 GTK# 进行了调查,但有相当多的依赖项我不想安装,除非 GTK# 是最佳选择。这是我的 GTK# 尝试,由于缺少依赖项,目前尚未测试:
internal class CrossPlatformKeyboardEvents : DrawingArea, IKeyboardEvents
{
public event CharEnteredHandler CharEntered = delegate { };
public event KeyEventHandler KeyDown = delegate { };
public event KeyEventHandler KeyUp = delegate { };
public CrossPlatformKeyboardEvents()
{
Application.Init();
Application.Run();
AddEvents((int)EventMask.KeyPressMask);
AddEvents((int)EventMask.KeyReleaseMask);
}
[ConnectBefore]
protected override bool OnKeyPressEvent(EventKey evnt)
{
if (IsPastEvent(evnt))
{
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_PASTE_CODE));
return base.OnKeyPressEvent(evnt);
}
switch (evnt.Key)
{
case Key.Return:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_RETURNKEY_CODE));
break;
case Key.BackSpace:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_BACKSPACE_CODE));
break;
case Key.Tab:
CharEntered(null, new CharEnteredEventArgs(KeyboardDispatcher.CHAR_TAB_CODE));
break;
}
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyDown(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyPressEvent(evnt);
}
[ConnectBefore]
protected override bool OnKeyReleaseEvent(EventKey evnt)
{
var keyCode = GetXNAKey(evnt.Key);
if (keyCode != Keys.None)
KeyUp(null, new XNAKeyEventArgs(keyCode));
return base.OnKeyReleaseEvent(evnt);
}
private bool IsPastEvent(EventKey evnt)
{
return (evnt.State & ModifierType.ControlMask) > 0 && (evnt.Key == Key.V || evnt.Key == Key.v);
}
private Keys GetXNAKey(Key key)
{
if ((key >= Key.Key_0 && key <= Key.Key_9) ||
(key >= Key.A && key <= Key.Z))
{
return (Keys) key;
}
if (key >= Key.a && key <= Key.z)
{
return (Keys) (key - 32);
}
return Keys.None;
//switch (key)
//{
//}
}
我通过在 MonoGame 中使用 TextInput event on GameWindow 解决了这个问题。
这是一个扩展,它是 MonoGame 的一部分,但不是 Xna 的一部分。目前,我有 2 个项目——一个用于标准 Xna,一个用于 Monogame。 Monogame 项目链接到我的 Xna 项目中的所有文件,因此它们共享相同的代码。
由于我的项目设置方式,我还必须添加一个编译器标志 (MONO),以排除此代码针对标准 XNA 进行编译。
完整 class(替换问题中发布的 CrossPlatformKeyboardEvents):
internal sealed class MonoGameKeyboardEvents : IKeyboardEvents
{
public event CharEnteredHandler CharEntered;
private readonly GameWindow _window;
public MonoGameKeyboardEvents(GameWindow window)
{
_window = window;
#if !MONO
if (CharEntered != null) //hide warning for "member is not used"
CharEntered(null, null);
#else
_window.TextInput += GameWindow_TextInput;
#endif
}
#if MONO
private void GameWindow_TextInput(object sender, TextInputEventArgs e)
{
if (CharEntered != null)
{
CharEntered(null, new CharEnteredEventArgs(e.Character));
}
}
~MonoGameKeyboardEvents()
{
Dispose(false);
}
#endif
public void Dispose()
{
#if !MONO
}
#else
Dispose(true);
}
private void Dispose(bool disposing)
{
if (!disposing) return;
_window.TextInput -= GameWindow_TextInput;
}
#endif
}