Вот класс KeyboardHandler
, который я написал для QuickStart Game Engine .Он хранит список состояний клавиш из предыдущего кадра, а затем сравнивает его со списком ключей из текущего кадра.На основании изменений в последнем кадре будет отправлено событие для каждой нажатой, удерживаемой или отпущенной клавиши.Он отправляет только эти 3 типа событий, но в каждом событии он содержит ключ, связанный с этим событием.
Теперь, если вы заботитесь только о конкретных ключах, вы можете использовать другой класс, который я написал, под названием 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
в любой форме, а не в формате сообщений, показанном в этом примере.Обратите внимание на функцию Press
, показанную снова ниже, поэтому мы проверяем, зарегистрирован ли ключ для.Вы можете поместить свой собственный код в этот раздел, чтобы запустить событие, которое вы слушаете, так что теперь вы получаете это событие только для ключей, которые вам небезразличны.
private bool Press(Keys keyType)
{
InputButton buttonRequested;
if (ButtonFromType(keyType, out buttonRequested))
{
// Your event sender could go here
return true;
}
else
{
return false;
}
}
Надеемся, вы получите общую концепциюэто.