2012-02-23 31 views
1

我有以下的僞代碼:每天更新()如果一個鍵被按下,持有或比可以提高相應的事件發佈期間如何激發一個非常特殊的KeyPressed事件?

public void Update() 
{ 
    if (pressed) 
     OnKeyPressed(key); 
    if (held) 
     OnKeyHeld(key); 
    if (released) 
     OnKeyReleased(key) 
} 

這樣。這是OnKeyPressed方法的實際代碼:

public void OnKeyPressed(Keys key) 
{ 
    EventHandler<InputEventArgs> handler = m_keyPressed; 

    if (handler != null) 
    { 
     handler(this, new InputEventArgs(key)); 
    } 
} 

然而,這是不是真的是我想要,因爲我並不關心,如果被按下的鍵。我所關心的是如果按下PARTICULAR鍵。我怎樣才能編寫這個來實現這個目標,而不會創建一個荒謬的事件量(每個要綁定的鍵的一個)?


RE:Nic-

行,所以我結合類和這裏的所產生的僞代碼:

public void OnKeyPressed(Keys key) 
{ 
    if(m_boundKeys.ContainsKey(key)) 
    { 
     //Fire event 
     keyPressed(this, EventArgs.Empty); 
    } 
} 

現在,上述問題是本次會議被解僱只不過是一個keyPressed事件。這不是A_keyPressed或B_keyPressed事件。我可以向keyPressed事件註冊事件,但這意味着每當按下任何註冊密鑰時,每個訂閱者都會收到一個keyPressed事件。

我在尋找:

public void OnKeyPressed(Keys key) 
{ 
    if(m_boundKeys.ContainsKey(key)) 
    { 
     //Specific key event based on key 
    } 
} 
+1

我會考慮在這篇文章中描述的方法 - http://stackoverflow.com/questions/1707040/handling-function-key-press – TheHorse1234 2012-02-23 02:34:14

+0

你可以添加一個方法或屬性來設置所需的鍵/鍵? 在Update()中,您可以根據所需的鍵/鍵進行過濾。 – linquize 2012-02-23 02:32:55

+0

呃...我可以過濾它,但是當它按下任何鍵時,它仍然會觸發KeyPressed事件。我想要的是更多的沿UpKeyPressed,DownKeyPressed等 – Kittoes0124 2012-02-23 02:35:50

回答

1

下面是一個KeyboardHandler類我寫的QuickStart Game Engine。它保存前一幀的關鍵狀態列表,然後比較當前幀的關鍵列表。根據最後一幀的變化,它會發出一個事件,每個按鍵被按下,保持或釋放。它只發出這三種類型的事件,但在每個事件中它都包含與此事件相關的密鑰。

現在,如果您只關心特定的密鑰,則可以使用我編寫的另一個課程,名爲InputPollingHandler,其中您只註冊您關心的密鑰,並且您將接收對這些註冊密鑰的調用。這也將允許您隨時輪詢任何已註冊密鑰的價值。 InputPollingHandler也允許你發起自己的事件,如果你想人爲地創建輸入事件,這是常見的事情,如系統測試,你想從錄製的事件複製輸入。 InputPollingHandler的代碼低於KeyboardHandler的代碼。

沒有任何辦法可以讓XNA通知您關於您可能關心的關鍵字,因此您使用助手類來偵聽所有關鍵字並僅向您發送關於所需關鍵字的信息。

/// <summary> 
/// This class handles keyboard input 
/// </summary> 
public class KeyboardHandler : InputHandler 
{ 
    /// <summary> 
    /// A list of keys that were down during the last update 
    /// </summary> 
    private List<KeyInfo> previousDownKeys; 

    /// <summary> 
    /// Holds the current keyboard state 
    /// </summary> 
    private KeyboardState currentKeyboardState; 

    /// <summary> 
    /// Creates a keyboard handler. 
    /// </summary> 
    /// <param name="game"></param> 
    public KeyboardHandler(QSGame game) 
     : base(game) 
    { 
     this.previousDownKeys = new List<KeyInfo>(); 
    } 

    /// <summary> 
    /// Reads the current keyboard state and processes all key messages required. 
    /// </summary> 
    /// <param name="gameTime"></param> 
    /// <remarks>This process may seem complicated and unefficient, but honestly most keyboards can 
    /// only process 4-6 keys at any given time, so the lists we're iterating through are relatively small. 
    /// So at the most we're doing 42 comparisons if 6 keys can be held at time. 42 comparisons only if the 
    /// 6 keys pressed during one frame are different than the 6 keys pressed on the next frame, which is 
    /// extremely unlikely.</remarks> 
    protected override void UpdateCore(GameTime gameTime) 
    { 
     this.currentKeyboardState = Keyboard.GetState(); 
     Keys[] currentlyPressed = this.currentKeyboardState.GetPressedKeys(); 
     bool[] isHeld = new bool[currentlyPressed.Length]; 

     for (int i = currentlyPressed.Length - 1; i >= 0; i--) 
     { 
      Keys key = currentlyPressed[i]; 

      // There were no keys down last frame, no need to loop through the last frame's state 
      if (this.previousDownKeys.Count == 0) 
      { 
       // Because no keys were down last frame, every key that is down this frame is newly pressed. 
       SendKeyMessage(MessageType.KeyDown, key, gameTime); 
      } 
      else 
      { 
       bool processed = false; 

       // Loop through all the keys that were pressed last frame, for comparison 
       for (int j = this.previousDownKeys.Count - 1; j >= 0; j--) 
       { 
        // If this key was used at all last frame then it is being held 
        if (key == this.previousDownKeys[j].key) 
        { 
         // We should keep track of the timer for each index in an array large enough for all keys 
         // so we can have a timer for how long each key has been held. This can come later. Until 
         // then keys are marked as held after one frame. - LordIkon 

         if (this.previousDownKeys[j].heldLastFrame == false) 
         { 
          // Send held message 
          isHeld[i] = true; 
          SendKeyMessage(MessageType.KeyHeld, key, gameTime); 
         } 
         else 
         { 
          isHeld[i] = true; 
         } 

         previousDownKeys.Remove(this.previousDownKeys[j]); // Remove this key from the previousDownKeys list 
         processed = true; 
         break; 
        } 
       } 

       // If key was un-processed throughout the loop, process message here as a new key press 
       if (processed == false) 
       { 
        SendKeyMessage(MessageType.KeyDown, key, gameTime); 
       } 
      } 
     } 

     // If there any keys left in the previous state after comparisons, it means they were released 
     if (this.previousDownKeys.Count > 0) 
     { 
      // Go through all keys and send 'key up' message for each one 
      for (int i = this.previousDownKeys.Count - 1; i >= 0; i--) 
      { 
       // Send released message 
       SendKeyMessage(MessageType.KeyUp, this.previousDownKeys[i].key, gameTime); 
      } 
     } 

     this.previousDownKeys.Clear();  // Clear the previous list of keys down 

     // Update the list of previous keys that are down for next loop 
     for (int i = currentlyPressed.Length - 1; i >= 0; i--) 
     { 
      Keys key = currentlyPressed[i]; 

      KeyInfo newKeyInfo; 
      newKeyInfo.key = key; 
      newKeyInfo.heldLastFrame = isHeld[i]; 

      this.previousDownKeys.Add(newKeyInfo); 
     } 
    } 

    /// <summary> 
    /// Sends a message containing information about a specific key 
    /// </summary> 
    /// <param name="keyState">The state of the key Down/Pressed/Up</param> 
    /// <param name="key">The <see cref="Keys"/> that changed it's state</param> 
    private void SendKeyMessage(MessageType keyState, Keys key, GameTime gameTime) 
    { 
     switch (keyState) 
     { 
      case MessageType.KeyDown: 
       { 
        MsgKeyPressed keyMessage = ObjectPool.Aquire<MsgKeyPressed>(); 
        keyMessage.Key = key; 
        keyMessage.Time = gameTime; 
        this.Game.SendMessage(keyMessage); 
       } 
       break; 
      case MessageType.KeyHeld: 
       { 
        MsgKeyHeld keyMessage = ObjectPool.Aquire<MsgKeyHeld>(); 
        keyMessage.Key = key; 
        keyMessage.Time = gameTime; 
        this.Game.SendMessage(keyMessage); 
       } 
       break; 
      case MessageType.KeyUp: 
       { 
        MsgKeyReleased keyMessage = ObjectPool.Aquire<MsgKeyReleased>(); 
        keyMessage.Key = key; 
        keyMessage.Time = gameTime; 
        this.Game.SendMessage(keyMessage); 
       } 
       break; 
      default: 
       break; 
     } 


    } 
} 

這裏是InputPollingHandler類。完整引擎中的版本更大,因爲它也支持鼠標和Xbox360遊戲手柄。

public class InputPollingHandler 
{ 
    private QSGame game; 

    /// <summary> 
    /// Stores all <see cref="Keys"/> that are specifically listened for. 
    /// </summary> 
    private Dictionary<Keys, InputButton> keys; 

    /// <summary> 
    /// Create an instance of an input polling handler 
    /// </summary> 
    /// <param name="Game"></param> 
    public InputPollingHandler(QSGame Game) 
    { 
     this.game = Game; 

     this.keys = new Dictionary<Keys, InputButton>();   

     this.game.GameMessage += this.Game_GameMessage; 
    } 

    /// <summary> 
    /// Add an input listener for a keyboard key. 
    /// </summary> 
    /// <param name="keyType">Key to listen for</param> 
    public void AddInputListener(Keys keyType) 
    { 
     InputButton newButton = new InputButton(); 
     this.keys.Add(keyType, newButton); 
    } 

    /// <summary> 
    /// Acquire a keyboard key 
    /// </summary> 
    /// <param name="keyType">Key to acquire</param> 
    /// <param name="buttonRequested">Returns the <see cref="InputButton"/> requested</param> 
    /// <returns>True if that button was registered for listening</returns> 
    private bool ButtonFromType(Keys keyType, out InputButton buttonRequested) 
    { 
     return this.keys.TryGetValue(keyType, out buttonRequested); 
    } 

    /// <summary> 
    /// Check if a keyboard key is currently being held 
    /// </summary> 
    /// <param name="keyType">Key to check</param> 
    /// <returns>True if button is being held</returns> 
    public bool IsHeld(Keys keyType) 
    { 
     InputButton buttonRequested; 
     if (ButtonFromType(keyType, out buttonRequested)) 
     { 
      return buttonRequested.IsHeld; 
     } 
     else 
     { 
      // This should be converted to an error that doesn't break like an exception does. 
      throw new Exception("This key does not have a listener. It must have a listener before it can be used."); 
      //return false; 
     } 
    } 

    /// <summary> 
    /// Check if a keyboard key is in the down state (was just pressed down). 
    /// </summary> 
    /// <param name="keyType">Keyboard key to check</param> 
    /// <returns>True if key has just been pressed down</returns> 
    public bool IsDown(Keys keyType) 
    { 
     InputButton buttonRequested; 
     if (ButtonFromType(keyType, out buttonRequested)) 
     { 
      return buttonRequested.IsDown; 
     } 
     else 
     { 
      // This should be converted to an error that doesn't break like an exception does. 
      throw new Exception("This key does not have a listener. It must have a listener before it can be used."); 
     } 
    } 

    /// <summary> 
    /// Check if a keyboard key is in the up state (not pressed down or held). 
    /// </summary> 
    /// <param name="keyType">Keyboard key to check</param> 
    /// <returns>True if button is up</returns> 
    public bool IsUp(Keys keyType) 
    { 
     InputButton buttonRequested; 
     if (ButtonFromType(keyType, out buttonRequested)) 
     { 
      return buttonRequested.IsUp; 
     } 
     else 
     { 
      // This should be converted to an error that doesn't break like an exception does. 
      throw new Exception("This key does not have a listener. It must have a listener before it can be used."); 
      //return false; 
     } 
    } 

    /// <summary> 
    /// Press down a keyboard key in the polling handler (not the actual key). 
    /// </summary> 
    /// <param name="keyType">Key to press</param> 
    /// <returns>True if key has been registered with a listener</returns> 
    /// <remarks>Private because only the message system should use this function</remarks> 
    private bool Press(Keys keyType) 
    { 
     InputButton buttonRequested; 
     if (ButtonFromType(keyType, out buttonRequested)) 
     { 
      buttonRequested.Press(); 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

    /// <summary> 
    /// Release a keyboard key in the polling handler (not the actual gamepad button). 
    /// </summary> 
    /// <param name="keyType">Keyboard key to release</param> 
    /// <returns>True if key has been registered with a listener.</returns> 
    /// <remarks>Private because only the message system should use this function</remarks> 
    private bool Release(Keys keyType) 
    { 
     InputButton buttonRequested; 
     if (ButtonFromType(keyType, out buttonRequested)) 
     { 
      buttonRequested.Release(); 
      buttonRequested.SetHeld(false); 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

    /// <summary> 
    /// Set the held state of this keyboard key in the polling handler. This occurs whenever a key is being held. 
    /// </summary> 
    /// <param name="keyType">Keyboard key to hold</param> 
    /// <param name="heldState">True for 'held', false to 'unhold'</param> 
    /// <returns>True if key has been registered with a listener</returns> 
    private bool SetHeld(Keys keyType, bool heldState) 
    { 
     InputButton buttonRequested; 
     if (ButtonFromType(keyType, out buttonRequested)) 
     { 
      buttonRequested.SetHeld(heldState); 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 

    /// <summary> 
    /// Set the lockable state of this keyboard key in the polling handler. Locked keys do not repeat or report as 'held'. 
    /// </summary> 
    /// <param name="keyType">Keyboard key for which to set lockable state</param> 
    /// <param name="lockableState">'true' will set this key to 'lockable'</param> 
    /// <returns>True if this key has been registered with a listener</returns> 
    public bool SetLockable(Keys keyType, bool lockableState) 
    { 
     InputButton buttonRequested; 
     if (ButtonFromType(keyType, out buttonRequested)) 
     { 
      buttonRequested.SetLockable(lockableState); 
      return true; 
     } 
     else 
     { 
      // This should be converted to an error that doesn't break like an exception does. 
      throw new Exception("This key does not have a listener. It must have a listener before it can be used."); 
      //return false; 
     } 
    } 

    /// <summary> 
    /// Message handler for the input polling handler. 
    /// </summary> 
    /// <param name="message">Incoming message</param> 
    private void Game_GameMessage(IMessage message) 
    { 
     switch (message.Type) 
     { 
      case MessageType.KeyDown: 
       MsgKeyPressed keyDownMessage = message as MsgKeyPressed; 
       message.TypeCheck(keyDownMessage); 

       Press(keyDownMessage.Key); 
       break; 

      case MessageType.KeyUp: 
       MsgKeyReleased keyUpMessage = message as MsgKeyReleased; 
       message.TypeCheck(keyUpMessage); 

       Release(keyUpMessage.Key); 
       break; 

      case MessageType.KeyHeld: 
       MsgKeyHeld keyPressMessage = message as MsgKeyHeld; 
       message.TypeCheck(keyPressMessage); 

       SetHeld(keyPressMessage.Key, true); 
       break; 
     } 
    } 
} 

這個InputPollingHandler監聽特定於遊戲引擎的密鑰消息。在您的版本中,您只需將KeyboardHandler以任何您想要的形式發送到InputPollingHandler,而不是以本示例中顯示的消息格式。請注意0​​函數,如下所示,所以我們檢查是否註冊了密鑰。您可以將自己的代碼放在該部分中以觸發您正在收聽的事件,因此現在只有您關心的密鑰纔會收到此事件。

private bool Press(Keys keyType) 
{ 
    InputButton buttonRequested; 
    if (ButtonFromType(keyType, out buttonRequested)) 
    { 
     // Your event sender could go here 
     return true; 
    } 
    else 
    { 
     return false; 
    } 
} 

希望你會得到它背後的一般概念。

+0

我已經有這樣的事情。我只是想更進一步,並能夠觸發/訂閱A_KeyDown,B_KeyDown等等,而無需創建十億個事件。創建這麼多事件只會是愚蠢的,更不用說維護問題了,因爲我希望玩家能夠綁定他們選擇的東西。 – Kittoes0124 2012-02-23 21:00:38

+0

InputPollingHandler允許您在您已經訂閱的某些鍵被按下時註冊通知。您可以有效地組合上述類,並且在比較從最後一幀到當前幀的所有關鍵事件時,只能爲註冊的鍵發送事件。 – 2012-02-23 21:22:01

+0

是的,但我將如何去創建這些事件?從技術上講,事件在用戶實際註冊之前不存在。我可以有一個KeyPressed事件來啓動輪詢器,然後讓輪詢器過濾掉綁定的鍵,但是如何爲每個綁定鍵創建/引發事件? – Kittoes0124 2012-02-23 22:25:21