2008-12-17 18 views
15

Okay, also im Grunde möchte ich Tastatur Text abrufen können. Wie die Eingabe von Text in ein Textfeld oder etwas. Ich schreibe nur mein Spiel für Windows. Ich habe die Verwendung von Guide.BeginShowKeyboardInput ignoriert, weil es das Gefühl eines eigenständigen Spiels unterbricht, und die Tatsache, dass der Guide immer XBOX-Schaltflächen anzeigt, scheint mir auch nicht richtig zu sein. Ja, es ist der einfachste Weg, aber ich mag es nicht.XNA - Tastatur Texteingabe

Als nächstes habe ich versucht mit System.Windows.Forms.NativeWindow. Ich habe eine Klasse erstellt, die von ihr geerbt hat und ihr das Handle-Fenster übergeben, die WndProc-Funktion implementiert, um WM_CHAR (oder WM_KEYDOWN) zu finden, obwohl WndProc für andere Nachrichten aufgerufen wurde, WM_CHAR und WM_KEYDOWN haben das nie getan. Also musste ich diese Idee aufgeben, und außerdem referenzierte ich auch die ganzen Windows-Formulare, was unnötigen Speicherbedarf bedeutete.

Also war meine letzte Idee, einen Thread-Level, Low-Level-Tastatur-Hook zu erstellen. Dies war bisher das erfolgreichste. Ich bekomme WM_KEYDOWN Nachricht, (noch nicht WM_CHAR versucht) den virtuellen Schlüsselcode mit Win32 Funktion MapVirtualKey zu einem Zeichen zu übersetzen. Und ich bekomme meinen Text! (Ich drucke gerade mit Debug.Write im Moment)

Ein paar Probleme obwohl. Es ist, als hätte ich eine Feststelltaste und eine nicht reagierende Umschalttaste. (Natürlich ist es nicht, es gibt nur einen virtuellen Schlüsselcode pro Schlüssel, also hat es nur einen Ausgang) und es fügt einen Overhead hinzu, da es sich an die Windows-Hook-Liste anfügt und nicht so schnell ist wie ich D wie es sein soll, aber die Langsamkeit könnte mehr auf Debug.Write zurückzuführen sein.

Hat sich noch jemand angesprochen und es gelöst, ohne auf eine Bildschirmtastatur zurückgreifen zu müssen? oder hat jemand weitere Ideen, die ich ausprobieren kann?

danke im voraus.

Frage von Jimmy fragte

Vielleicht verstehen Ich bin nicht die Frage, aber warum können Sie verwenden nicht die XNA Keyboard und KeyboardState Klassen?

Mein Kommentar:

Es ist, weil wenn Sie keystates lesen können, Sie keinen Zugriff auf getippten Text bekommen, wie und wie sie durch den Benutzer eingegeben wird.

Also lassen Sie mich weiter klären. Ich möchte implementieren, in der Lage zu sein, Texteingabe vom Benutzer zu lesen, als ob sie in Textbox Windows eingibt. Die Keyboard- und die KeyboardState-Klasse erhalten die Zustände aller Schlüssel, aber ich müsste jeden Schlüssel und jede Kombination seiner Zeichendarstellung zuordnen. Dies fällt um, wenn der Benutzer nicht die gleiche Tastatursprache wie ich speziell mit Symbolen verwende (meine doppelten Anführungszeichen ist Shift + 2, während amerikanische Tastaturen ihre irgendwo in der Nähe der Return-Taste haben).


es scheint mein Fenster Haken war der Weg zu gehen, nur der Grund, warum ich nicht WM_CHAR bekommen ist, weil die XNA Meldungsverteilschleife Nachricht nicht übersetzen macht.

Hinzufügen von Translate in wann immer ich eine WM _ keyDown Nachricht empfangen bedeuten, dass ich meine WM_CHAR Nachricht bekam, habe ich dann dies ein Zeichen eingegeben Ereignis in meiner MessageHook Klasse zu feuern, die meine tastaturpuffer Klasse abonniert hatten, das dann, dass die Puffer auf einem Text-Puffer: D (oder StringBuilder, aber das Ergebnis ist das gleiche)

So habe ich alles funktioniert, wie ich will.

Vielen Dank an Jimmy für einen Link zu einem sehr informativen Thread.

Antwort

1

Vielleicht verstehe ich die Frage nicht, aber warum können Sie nicht die XNA Keyboard und KeyboardState Klassen verwenden?

+0

Es liegt daran, obwohl Sie Keystates lesen können, können Sie nicht zugreifen, um Text eingegeben wie und wie es vom Benutzer eingegeben wird. – Sekhat

+3

Ah ich sehe. In der Vergangenheit habe ich eine Utility-Klasse für diesen Zweck geschrieben, ähnlich der auf http://www.gamedev.net/community/forums/topic.asp?topic_id=457783 – Jimmy

+0

Danke, es scheint, mein Fenster Hook war Der Weg zu gehen, nur der Grund, warum ich WM_CHAR nicht erhalten habe, ist, weil die XNA-Nachrichtenpumpe keine Nachricht übersetzt. Ich werde eine Geige herum und sehen, was ich sonst noch bekommen kann :) – Sekhat

9

für einen Windows-Haken in XNA Hinzufügen

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.InteropServices; 
using System.Diagnostics; 
using System.Reflection; 

/* Author: Sekhat 
* 
* License: Public Domain. 
* 
* Usage: 
* 
* Inherit from this class, and override the WndProc function in your derived class, 
* in which you handle your windows messages. 
* 
* To start recieving the message, create an instance of your derived class, passing in the 
* window handle of the window you want to listen for messages for. 
* 
* in XNA: this would be the Game.Window.Handle property 
* in Winforms Form.Handle property 
*/ 

namespace WindowsHookExample 
{ 
    public abstract class WindowsHook : IDisposable 
    { 
     IntPtr hHook; 
     IntPtr hWnd; 
     // Stored here to stop it from getting garbage collected 
     Win32.WndProcDelegate wndProcDelegate; 

     public WindowsHook(IntPtr hWnd) 
     { 
      this.hWnd = hWnd; 

      wndProcDelegate = WndProcHook; 

      CreateHook(); 
     } 

     ~WindowsHook() 
     { 
      Dispose(false); 
     } 

     private void CreateHook() 
     { 

      uint threadId = Win32.GetWindowThreadProcessId(hWnd, IntPtr.Zero); 

      hHook = Win32.SetWindowsHookEx(Win32.HookType.WH_CALLWNDPROC, wndProcDelegate, IntPtr.Zero, threadId); 

     } 

     private int WndProcHook(int nCode, IntPtr wParam, ref Win32.Message lParam) 
     { 
      if (nCode >= 0) 
      { 
       Win32.TranslateMessage(ref lParam); // You may want to remove this line, if you find your not quite getting the right messages through. This is here so that WM_CHAR is correctly called when a key is pressed. 
       WndProc(ref lParam); 
      } 

      return Win32.CallNextHookEx(hHook, nCode, wParam, ref lParam); 
     } 

     protected abstract void WndProc(ref Win32.Message message); 

     #region Interop Stuff 
     // I say thankya to P/Invoke.net. 
     // Contains all the Win32 functions I need to deal with 
     protected static class Win32 
     { 
      public 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 
      } 

      public struct Message 
      { 
       public IntPtr lparam; 
       public IntPtr wparam; 
       public uint msg; 
       public IntPtr hWnd; 
      } 

      /// <summary> 
      /// Defines the windows proc delegate to pass into the windows hook 
      /// </summary>     
      public delegate int WndProcDelegate(int nCode, IntPtr wParam, ref Message m); 

      [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
      public static extern IntPtr SetWindowsHookEx(HookType hook, WndProcDelegate callback, 
       IntPtr hMod, uint dwThreadId); 

      [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
      public static extern bool UnhookWindowsHookEx(IntPtr hhk); 

      [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
      public static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, ref Message m); 

      [DllImport("coredll.dll", SetLastError = true)] 
      public static extern IntPtr GetModuleHandle(string module); 

      [DllImport("user32.dll", EntryPoint = "TranslateMessage")] 
      public extern static bool TranslateMessage(ref Message m); 

      [DllImport("user32.dll")] 
      public extern static uint GetWindowThreadProcessId(IntPtr window, IntPtr module); 
     } 
     #endregion 

     #region IDisposable Members 

     public void Dispose() 
     { 
      Dispose(true); 
     } 

     private void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       // Free managed resources here 
      } 
      // Free unmanaged resources here 
      if (hHook != IntPtr.Zero) 
      { 
       Win32.UnhookWindowsHookEx(hHook); 
      } 
     } 

     #endregion 
    } 
} 
+0

Die Reihenfolge der Mitglieder dieser Message-Struktur sind falsch und führen zu unleserlichen Ergebnissen. Sie sollten in dieser Reihenfolge sein: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644964(v=vs.85).aspx – Trevor

+0

@Rovert eingestellt – Sekhat

0

Diese Seite ist auf der Google-Ergebnis über WM_CHAR Interception in xna, also lasse ich hier einige Notiz. Vielleicht wird das für andere nützlich sein (wenn sie mein Englisch verstehen können =))).

Ich versuche Code mit Windowshook von Sekhat, aber es scheint, dass WH_GETMESSAGE an SetWindowsHookEx anstelle von Win32.HookType.WH_CALLWNDPROC übergeben werden sollte (nur mit WH_GETMESSAGE-Code lparaw wird auf Win32.Message zeigen).

Auch gibt es manchmal doppelte Nachrichten (mit wparam 0). (hier finden - http://msdn.microsoft.com/en-us/library/ms644981%28v=VS.85%29.aspx etwas über PM_NOREMOVE/PM_REMOVE in WPARAM)

Wenn ich so etwas wie dieses

hinzufügen
if (nCode >= 0 && wParam == 1) 
    { 
     Win32.TranslateMessage(ref lParam); 
     WndProc(ref lParam); 
    } 

wm_keypress WM_CHAR Vervielfältigung gestoppt (i nehme an 1 PM_NOREMOVE oder PM_REMOVE).

P.S. nuclex-Variante zeigt jetzt 404-Seite, kann aber mit Webarchive angezeigt werden. nuclex Variante funktioniert, aber es gebrochen mousewheel Verarbeitung von nativen XNA MouseState verursachen (auf XNA 3,1) = (

1

Hier ist ein einfacher Weg, IMO, die space, back, A-Z und dann die Sonderzeichen !,@,#,$,%,^,&,*,(,) zu haben. (Beachten Sie, müssen Sie System.Linq importieren) sind hier die Felder:

Keys[] keys; 
bool[] IskeyUp; 
string[] SC = { ")" , "!", "@", "#", "$", "%", "^", "&", "*", "("};//special characters 

Constructor:

keys = new Keys[38]; 
Keys[] tempkeys; 
tempkeys = Enum.GetValues(typeof(Keys)).Cast<Keys>().ToArray<Keys>(); 
int j = 0; 
for (int i = 0; i < tempkeys.Length; i++) 
{ 
    if (i == 1 || i == 11 || (i > 26 && i < 63))//get the keys listed above as well as A-Z 
    { 
     keys[j] = tempkeys[i];//fill our key array 
     j++; 
    } 
} 
IskeyUp = new bool[keys.Length]; //boolean for each key to make the user have to release the key before adding to the string 
for (int i = 0; i < keys.Length; i++) 
    IskeyUp[i] = true; 

Und schließlich die Update-Methode:

string result = ""; 

public override void Update(GameTime gameTime) 
{ 
    KeyboardState state = Keyboard.GetState(); 
    int i = 0; 
    foreach (Keys key in keys) 
    { 
     if (state.IsKeyDown(key)) 
     { 
      if (IskeyUp[i]) 
      { 
       if (key == Keys.Back && result != "") result = result.Remove(result.Length - 1); 
       if (key == Keys.Space) result += " "; 
       if (i > 1 && i < 12) 
       { 
        if (state.IsKeyDown(Keys.RightShift) || state.IsKeyDown(Keys.LeftShift)) 
         result += SC[i - 2];//if shift is down, and a number is pressed, using the special key 
        else result += key.ToString()[1]; 
       } 
       if (i > 11 && i < 38) 
       { 
        if (state.IsKeyDown(Keys.RightShift) || state.IsKeyDown(Keys.LeftShift)) 
         result += key.ToString(); 
        else result += key.ToString().ToLower(); //return the lowercase char is shift is up. 
       } 
      } 
      IskeyUp[i] = false; //make sure we know the key is pressed 
     } 
     else if (state.IsKeyUp(key)) IskeyUp[i] = true; 
     i++; 
    } 
    base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen); 
} 

Hopefull dies auch helfen. Ich persönlich dachte, es wäre einfacher zu benutzen als die Haken, und dies kann auch leicht für spezielle Ergebnisse (wie das Mischen der Schlüssel) geändert werden.

+0

Nur Problem mit diesem, ist, dass es ist nicht ereignisbasiert. Wenn der Benutzer also schneller als die Aktualisierungsrate eingibt, werden Tastendrücke nicht angenommen. – Sekhat

+0

@Sekhat, wenn die Update-Methode alle 16 ms stattfindet, sind das 60 Updates pro Sekunde. Ich bezweifle stark, dass ein Benutzer mehr als 60 Zeichen pro Sekunde eingeben könnte (das wären 3600 Zeichen pro Minute). :) Kaum jemand kann 700 CPM schreiben (siehe tipping-speed-test.aoeu.eu/?lang=de) Ich bin im 80. Perzentil und bekomme 330 CPM. – davidsbro

+0

Wenn ich einen Schlüssel unter 16ms drücke und loslasse, wird dein Code ihn niemals registrieren. Es ist einfacher als es klingt, vertrau mir. – Sekhat

Verwandte Themen