Вот решение для ввода, основанное на наборе , а не выборе символов .
Этот код использует собственный API-интерфейс win32 для обнаружения нажатий клавиш (включая управляющие символы)).
Код предоставлен Promit, пользователем gamedev.net, в этом сообщении .
Создать новый класс в своем проекте,назовите его CharEventArgs
и замените все внутри следующим (код по ссылке выше):
using System;
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
namespace EventInput
{
public class CharacterEventArgs : EventArgs
{
private readonly char character;
private readonly int lParam;
public CharacterEventArgs(char character, int lParam)
{
this.character = character;
this.lParam = lParam;
}
public char Character
{
get { return character; }
}
public int Param
{
get { return lParam; }
}
public int RepeatCount
{
get { return lParam & 0xffff; }
}
public bool ExtendedKey
{
get { return (lParam & (1 << 24)) > 0; }
}
public bool AltPressed
{
get { return (lParam & (1 << 29)) > 0; }
}
public bool PreviousState
{
get { return (lParam & (1 << 30)) > 0; }
}
public bool TransitionState
{
get { return (lParam & (1 << 31)) > 0; }
}
}
public class KeyEventArgs : EventArgs
{
private Keys keyCode;
public KeyEventArgs(Keys keyCode)
{
this.keyCode = keyCode;
}
public Keys KeyCode
{
get { return keyCode; }
}
}
public delegate void CharEnteredHandler(object sender, CharacterEventArgs e);
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
public static class EventInput
{
/// <summary>
/// Event raised when a character has been entered.
/// </summary>
public static event CharEnteredHandler CharEntered;
/// <summary>
/// Event raised when a key has been pressed down. May fire multiple times due to keyboard repeat.
/// </summary>
public static event KeyEventHandler KeyDown;
/// <summary>
/// Event raised when a key has been released.
/// </summary>
public static event KeyEventHandler KeyUp;
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
static bool initialized;
static IntPtr prevWndProc;
static WndProc hookProcDelegate;
static IntPtr hIMC;
//various Win32 constants that we need
const int GWL_WNDPROC = -4;
const int WM_KEYDOWN = 0x100;
const int WM_KEYUP = 0x101;
const int WM_CHAR = 0x102;
const int WM_IME_SETCONTEXT = 0x0281;
const int WM_INPUTLANGCHANGE = 0x51;
const int WM_GETDLGCODE = 0x87;
const int WM_IME_COMPOSITION = 0x10f;
const int DLGC_WANTALLKEYS = 4;
//Win32 functions that we're using
[DllImport("Imm32.dll")]
static extern IntPtr ImmGetContext(IntPtr hWnd);
[DllImport("Imm32.dll")]
static extern IntPtr ImmAssociateContext(IntPtr hWnd, IntPtr hIMC);
[DllImport("user32.dll")]
static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
/// <summary>
/// Initialize the TextInput with the given GameWindow.
/// </summary>
/// <param name="window">The XNA window to which text input should be linked.</param>
public static void Initialize(GameWindow window)
{
if (initialized)
throw new InvalidOperationException("TextInput.Initialize can only be called once!");
hookProcDelegate = new WndProc(HookProc);
prevWndProc = (IntPtr)SetWindowLong(window.Handle, GWL_WNDPROC,
(int)Marshal.GetFunctionPointerForDelegate(hookProcDelegate));
hIMC = ImmGetContext(window.Handle);
initialized = true;
}
static IntPtr HookProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
IntPtr returnCode = CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
switch (msg)
{
case WM_GETDLGCODE:
returnCode = (IntPtr)(returnCode.ToInt32() | DLGC_WANTALLKEYS);
break;
case WM_KEYDOWN:
if (KeyDown != null)
KeyDown(null, new KeyEventArgs((Keys)wParam));
break;
case WM_KEYUP:
if (KeyUp != null)
KeyUp(null, new KeyEventArgs((Keys)wParam));
break;
case WM_CHAR:
if (CharEntered != null)
CharEntered(null, new CharacterEventArgs((char)wParam, lParam.ToInt32()));
break;
case WM_IME_SETCONTEXT:
if (wParam.ToInt32() == 1)
ImmAssociateContext(hWnd, hIMC);
break;
case WM_INPUTLANGCHANGE:
ImmAssociateContext(hWnd, hIMC);
returnCode = (IntPtr)1;
break;
}
return returnCode;
}
}
}
В вашем Game1.cs (или как вы решили его назвать)поместите using EventInput;
сверху.
В вашем методе Initialize добавьте следующее:
EventInput.EventInput.Initialize(this.Window);
EventInput.EventInput.CharEntered += new CharEnteredHandler(EventInput_CharEntered);
В вашем Game1.cs (опять же, как вы его назвали), добавьте соответствующий обработчик:
void EventInput_CharEntered(object sender, CharacterEventArgs e)
{
if (char.IsControl(e.Character))
{
switch (e.Character)
{
case '\b':
if (hiScoreName.Length > 0)
{
hiScoreName = hiScoreName.Substring(0, hiScoreName.Length - 1);
}
break;
case '\r':
//enter
break;
}
}
else
{
hiScoreName += e.Character;
}
}
Наконец, в вашем методе Draw () используйте аналогичный код для рисования строки:
spriteBatch.Begin();
spriteBatch.DrawString(spriteFont, hiScoreName, new Vector2(20f, 20f), Color.Black);
spriteBatch.End();
Резюме:
Это в значительной степени лишает законной силы все остальное в этой теме (включая ваш код и все, что я опубликовал в другом ответе), bпотому что он использует другой способ получения ввода от пользователя.
Как только вы вставите код в CharEventArgs.cs, вы можете безопасно закрыть его и никогда не открывать снова , он будет делать то, чтодолжно.
Основная точка , где вы обрабатываете ввод , - это ваш обработчик событий EventInput_CharEntered
, где вы проверяете, какой символ нажимается, и воздействуете на него.Вы должны поиграть с этим методом, добавить свой собственный код, чтобы вы могли, например, использовать его для ввода hiScoreName в одном случае, и использовать его для другого ввода текста в другом месте.
Вы можете увидеть в событииобработчик, я прокомментировал // ввод, так что вы могли бы, скажем, поместить вызов функции для обработки сохранения hiScoreName там!
Также обратите внимание, что CharacterEventArgs e
содержит информацию о дополнительных модификаторах ,такие как Alt, количество повторов и т. д. Вы можете использовать их для добавления дополнительной логики в вашу программу.Полезно прочитать о том, как Windows обрабатывает нажатия клавиш по этой ссылке msdn (очень понятно).
Вы также можете зарегистрировать события KeyPressed и KeyReleased в классе EventInput, если они вам нужны.
Я протестировал код, который выложил здесь, и он работает для меня!
Веселитесь!
РЕДАКТИРОВАТЬ : если вы хотите использовать символы Юникода (которые имеют отношениена ваш язык, т. е. я использую ščćžđ), вам нужно вызывать DLLImports следующим образом:
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
Таким образом, вы должны изменить все вызовы DllImport для включения CharSet = CharSet.Unicode
.
Такжене забудьте расширить диапазон шрифта spritefont, включив в него те символы, которые вы, возможно, захотите отобразить.