下面是一個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;
}
}
希望你會得到它背後的一般概念。
我會考慮在這篇文章中描述的方法 - http://stackoverflow.com/questions/1707040/handling-function-key-press – TheHorse1234 2012-02-23 02:34:14
你可以添加一個方法或屬性來設置所需的鍵/鍵? 在Update()中,您可以根據所需的鍵/鍵進行過濾。 – linquize 2012-02-23 02:32:55
呃...我可以過濾它,但是當它按下任何鍵時,它仍然會觸發KeyPressed事件。我想要的是更多的沿UpKeyPressed,DownKeyPressed等 – Kittoes0124 2012-02-23 02:35:50