我有以下伪代码:
public void Update()
{
if (pressed)
OnKeyPressed(key);
if (held)
OnKeyHeld(key);
if (released)
OnKeyReleased(key)
}
因此,在每次Update()期间,如果按下,保持或释放按键,则可以引发相应的事件。这是OnKeyPressed方法的实际代码:
public void OnKeyPressed(Keys key)
{
EventHandler<InputEventArgs> handler = m_keyPressed;
if (handler != null)
{
handler(this, new InputEventArgs(key));
}
}
但是,这并不是我想要的,因为我不一定关心是否按下了按键。我关心的是如果按下一个特殊的键。如何在不创建荒谬的事件数量的情况下对此进行编码以实现此目标(每个键我想绑定一个)?
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
}
}
答案 0 :(得分:1)
这是我为QuickStart Game Engine撰写的KeyboardHandler
课程。它保留前一帧中的键状态列表,然后将其与当前帧中的键列表进行比较。根据最后一帧的变化,它将为按下,保持或释放的每个键发送一个事件。它只发送这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;
}
}
希望你能得到它背后的一般概念。