2008-12-17 136 views
15

好吧,所以基本上我想能夠檢索鍵盤文本。就像在文本字段中輸入文本一樣。我只爲Windows編寫我的遊戲。 我已經忽略了使用Guide.BeginShowKeyboardInput,因爲它打破了自包含遊戲的感覺,而且指南總是顯示XBOX按鈕的事實對我來說也不合適。是的,這是最簡單的方法,但我不喜歡它。XNA - 鍵盤文本輸入

接下來我嘗試使用System.Windows.Forms.NativeWindow。我創建了一個繼承自它的類,並將其傳遞給遊戲窗口句柄,實現了WndProc函數來捕獲WM_CHAR(或WM_KEYDOWN),儘管WndProc被其他消息調用,WM_CHAR和WM_KEYDOWN從未做過。所以我不得不放棄這個想法,此外,我還引用了整個Windows窗體,這意味着不必要的內存佔用膨脹。

所以我最後的想法是創建一個線程級別,低級別的鍵盤鉤子。迄今爲止,這是最成功的。我得到了WM_KEYDOWN消息,(還沒有嘗試過WM_CHAR)將虛函數與Win32函數MapVirtualKey轉換爲字符。我得到我的文本! (我現在只是用Debug.Write打印)

雖然有一些問題。就好像我鎖定了大寫字母,並且沒有響應的切換鍵。 (當然不是,每個鍵只有一個虛擬鍵碼,所以翻譯它只有一個輸出),它增加了開銷,因爲它將自己附加到Windows鉤子列表中,並且不像我' d喜歡它,但緩慢可能更多由於Debug.Write。

有沒有其他人接近這個並解決它,而不必訴諸於屏幕鍵盤?還是有人有進一步的想法讓我嘗試?

在此先感謝。

問題吉米

問也許我不理解的問題,但爲什麼不能使用XNA鍵盤和KeyboardState類?

我的評論:

這是因爲雖然你可以閱讀keystates,你不能獲得輸入的文字作爲以及它是如何由用戶輸入。

所以讓我進一步澄清。我想實現能夠從用戶讀取文本輸入,就好像他們正在輸入文本框是Windows一樣。鍵盤和KeyboardState類獲得所有鍵的狀態,但我必須將每個鍵和組合映射到它的字符表示。當用戶不使用與我特別使用相同的鍵盤語言(當我的雙引號是shift + 2,而美國的鍵盤有他們的位置在返回鍵附近的某處)時,這種情況會發生。


看來我的窗口鉤子是要走的路,只是我沒有得到WM_CHAR的原因是因爲XNA消息泵沒有做翻譯的消息。

中,每當我收到了WM _ KEYDOWN消息意味着我得到了我的WM_CHAR消息添加的TranslateMessage,然後我用這個在我的KeyboardBuffer類曾訂閱我的MessageHook類,然後緩存是一個火一個字符輸入事件文本緩衝區:D(或StringBuilder,但結果是相同的)

所以我有這一切工作,因爲我想。

非常感謝吉米提供了一個非常翔實的主題鏈接。

回答

1

也許我不明白這個問題,但爲什麼你不能使用XNA鍵盤和KeyboardState類?

+0

這是因爲雖然您可以閱讀關鍵狀態,但無法訪問鍵入的文本以及用戶如何鍵入文本。 – Sekhat 2008-12-17 17:29:29

+3

啊我明白了。在過去,我爲此寫了一個工具類,類似於http://www.gamedev.net/community/forums/topic.asp?topic_id=457783 – Jimmy 2008-12-17 17:40:21

+0

的工具類謝謝,它似乎是我的窗口鉤子要走的路,只是我沒有得到WM_CHAR的原因是因爲XNA消息泵不會翻譯消息。我將有一個小提琴,看看我能得到什麼:) – Sekhat 2008-12-18 09:55:24

9

對於XNA

添加Windows鉤子
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

這頁上的谷歌造成約在XNA WM_CHAR攔截頂部,所以我離開這裏的一些注意事項。也許這會對其他人有用(如果他們能夠理解我的英語=)))。

我嘗試從Sekhat windowshook代碼,但似乎應該有WH_GETMESSAGE傳遞而不是調用SetWindowsHookEx Win32.HookType.WH_CALLWNDPROC(僅WH_GETMESSAGE代碼lparaw將指向Win32.Message)。

還有一些重複的消息(與wparam 0)。 (看這裏 - http://msdn.microsoft.com/en-us/library/ms644981%28v=VS.85%29.aspx 一些關於PM_NOREMOVE/PM_REMOVE在WPARAM)

當我添加這樣的事情

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

wm_keypress WM_CHAR複製停止(我supose 1 PM_NOREMOVE或PM_REMOVE)。

P.S. nuclex變體現在顯示404頁面,但可以通過webarchive查看。 nuclex變種作品,但它從本地XNA MouseState(在XNA 3.1)導致破鼠標滾輪加工=(

1

這裏有一個簡單的方法,國際海事組織,有space,back,A-Z,然後是特殊字符!,@,#,$,%,^,&,*,(,)。 (請注意,您需要導入System.Linq)這裏的字段:

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

構造:

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; 

最後,更新方法:

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); 
} 

厚福本很好的幫助。我個人認爲它比鉤子更容易使用,而且這也可以很容易地修改爲特殊結果(例如洗牌鍵)。