如何减慢或停止XNA中的按键操作

我已经开始使用XNA Framework编写游戏,并且遇到了一些我不知道如何正确解决的简单问题。

我正在使用Texture2D显示菜单并使用键盘(或游戏手柄)我更改了所选的菜单项。 我的问题是用于在菜单项之间切换的当前函数太快了。 我可能会单击向下按钮,它将关闭5或6个菜单项(由于Update()被多次调用,因此更新所选项目)。

ex. (> indicate selected) > MenuItem1 MenuItem2 MenuItem3 MenuItem4 MenuItem5 I press the down key for just a second), then I have this state: MenuItem1 MenuItem2 MenuItem3 > MenuItem4 MenuItem5 What I want is (until I press the key again) MenuItem1 > MenuItem2 MenuItem3 MenuItem4 MenuItem5 

我正在寻找的方法是让玩家多次点击向上/向下键以便从一个菜单项转到另一个菜单项,或者在进入下一个菜单项之前有一些最小的等待时间。

我已经构建了一个(大)类,可以帮助解决任何和所有XNA输入相关的任务,它可以让您轻松应对。

 using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; namespace YourNamespaceHere { ///  /// an enum of all available mouse buttons. ///  public enum MouseButtons { LeftButton, MiddleButton, RightButton, ExtraButton1, ExtraButton2 } public class InputHelper { private GamePadState _lastGamepadState; private GamePadState _currentGamepadState; #if (!XBOX) private KeyboardState _lastKeyboardState; private KeyboardState _currentKeyboardState; private MouseState _lastMouseState; private MouseState _currentMouseState; #endif private PlayerIndex _index = PlayerIndex.One; private bool refreshData = false; ///  /// Fetches the latest input states. ///  public void Update() { if (!refreshData) refreshData = true; if (_lastGamepadState == null && _currentGamepadState == null) { _lastGamepadState = _currentGamepadState = GamePad.GetState(_index); } else { _lastGamepadState = _currentGamepadState; _currentGamepadState = GamePad.GetState(_index); } #if (!XBOX) if (_lastKeyboardState == null && _currentKeyboardState == null) { _lastKeyboardState = _currentKeyboardState = Keyboard.GetState(); } else { _lastKeyboardState = _currentKeyboardState; _currentKeyboardState = Keyboard.GetState(); } if (_lastMouseState == null && _currentMouseState == null) { _lastMouseState = _currentMouseState = Mouse.GetState(); } else { _lastMouseState = _currentMouseState; _currentMouseState = Mouse.GetState(); } #endif } ///  /// The previous state of the gamepad. /// Exposed only for convenience. ///  public GamePadState LastGamepadState { get { return _lastGamepadState; } } ///  /// the current state of the gamepad. /// Exposed only for convenience. ///  public GamePadState CurrentGamepadState { get { return _currentGamepadState; } } ///  /// the index that is used to poll the gamepad. ///  public PlayerIndex Index { get { return _index; } set { _index = value; if (refreshData) { Update(); Update(); } } } #if (!XBOX) ///  /// The previous keyboard state. /// Exposed only for convenience. ///  public KeyboardState LastKeyboardState { get { return _lastKeyboardState; } } ///  /// The current state of the keyboard. /// Exposed only for convenience. ///  public KeyboardState CurrentKeyboardState { get { return _currentKeyboardState; } } ///  /// The previous mouse state. /// Exposed only for convenience. ///  public MouseState LastMouseState { get { return _lastMouseState; } } ///  /// The current state of the mouse. /// Exposed only for convenience. ///  public MouseState CurrentMouseState { get { return _currentMouseState; } } #endif ///  /// The current position of the left stick. /// Y is automatically reversed for you. ///  public Vector2 LeftStickPosition { get { return new Vector2( _currentGamepadState.ThumbSticks.Left.X, -CurrentGamepadState.ThumbSticks.Left.Y); } } ///  /// The current position of the right stick. /// Y is automatically reversed for you. ///  public Vector2 RightStickPosition { get { return new Vector2( _currentGamepadState.ThumbSticks.Right.X, -_currentGamepadState.ThumbSticks.Right.Y); } } ///  /// The current velocity of the left stick. /// Y is automatically reversed for you. /// expressed as: /// current stick position - last stick position. ///  public Vector2 LeftStickVelocity { get { Vector2 temp = _currentGamepadState.ThumbSticks.Left - _lastGamepadState.ThumbSticks.Left; return new Vector2(temp.X, -temp.Y); } } ///  /// The current velocity of the right stick. /// Y is automatically reversed for you. /// expressed as: /// current stick position - last stick position. ///  public Vector2 RightStickVelocity { get { Vector2 temp = _currentGamepadState.ThumbSticks.Right - _lastGamepadState.ThumbSticks.Right; return new Vector2(temp.X, -temp.Y); } } ///  /// the current position of the left trigger. ///  public float LeftTriggerPosition { get { return _currentGamepadState.Triggers.Left; } } ///  /// the current position of the right trigger. ///  public float RightTriggerPosition { get { return _currentGamepadState.Triggers.Right; } } ///  /// the velocity of the left trigger. /// expressed as: /// current trigger position - last trigger position. ///  public float LeftTriggerVelocity { get { return _currentGamepadState.Triggers.Left - _lastGamepadState.Triggers.Left; } } ///  /// the velocity of the right trigger. /// expressed as: /// current trigger position - last trigger position. ///  public float RightTriggerVelocity { get { return _currentGamepadState.Triggers.Right - _lastGamepadState.Triggers.Right; } } #if (!XBOX) ///  /// the current mouse position. ///  public Vector2 MousePosition { get { return new Vector2(_currentMouseState.X, _currentMouseState.Y); } } ///  /// the current mouse velocity. /// Expressed as: /// current mouse position - last mouse position. ///  public Vector2 MouseVelocity { get { return ( new Vector2(_currentMouseState.X, _currentMouseState.Y) - new Vector2(_lastMouseState.X, _lastMouseState.Y) ); } } ///  /// the current mouse scroll wheel position. /// See the Mouse's ScrollWheel property for details. ///  public float MouseScrollWheelPosition { get { return _currentMouseState.ScrollWheelValue; } } ///  /// the mouse scroll wheel velocity. /// Expressed as: /// current scroll wheel position - /// the last scroll wheel position. ///  public float MouseScrollWheelVelocity { get { return (_currentMouseState.ScrollWheelValue - _lastMouseState.ScrollWheelValue); } } #endif ///  /// Used for debug purposes. /// Indicates if the user wants to exit immediately. ///  public bool ExitRequested { #if (!XBOX) get { return ( (IsCurPress(Buttons.Start) && IsCurPress(Buttons.Back)) || IsCurPress(Keys.Escape)); } #else get { return (IsCurPress(Buttons.Start) && IsCurPress(Buttons.Back)); } #endif } ///  /// Checks if the requested button is a new press. ///  ///  /// The button to check. ///  ///  /// a bool indicating whether the selected button is being /// pressed in the current state but not the last state. ///  public bool IsNewPress(Buttons button) { return ( _lastGamepadState.IsButtonUp(button) && _currentGamepadState.IsButtonDown(button)); } ///  /// Checks if the requested button is a current press. ///  ///  /// the button to check. ///  ///  /// a bool indicating whether the selected button is being /// pressed in the current state and in the last state. ///  public bool IsCurPress(Buttons button) { return ( _lastGamepadState.IsButtonDown(button) && _currentGamepadState.IsButtonDown(button)); } ///  /// Checks if the requested button is an old press. ///  ///  /// the button to check. ///  ///  /// a bool indicating whether the selected button is not being /// pressed in the current state and is being pressed in the last state. ///  public bool IsOldPress(Buttons button) { return ( _lastGamepadState.IsButtonDown(button) && _currentGamepadState.IsButtonUp(button)); } #if (!XBOX) ///  /// Checks if the requested key is a new press. ///  ///  /// the key to check. ///  ///  /// a bool that indicates whether the selected key is being /// pressed in the current state and not in the last state. ///  public bool IsNewPress(Keys key) { return ( _lastKeyboardState.IsKeyUp(key) && _currentKeyboardState.IsKeyDown(key)); } ///  /// Checks if the requested key is a current press. ///  ///  /// the key to check. ///  ///  /// a bool that indicates whether the selected key is being /// pressed in the current state and in the last state. ///  public bool IsCurPress(Keys key) { return ( _lastKeyboardState.IsKeyDown(key) && _currentKeyboardState.IsKeyDown(key)); } ///  /// Checks if the requested button is an old press. ///  ///  /// the key to check. ///  ///  /// a bool indicating whether the selectde button is not being /// pressed in the current state and being pressed in the last state. ///  public bool IsOldPress(Keys key) { return ( _lastKeyboardState.IsKeyDown(key) && _currentKeyboardState.IsKeyUp(key)); } ///  /// Checks if the requested mosue button is a new press. ///  ///  /// teh mouse button to check. ///  ///  /// a bool indicating whether the selected mouse button is being /// pressed in the current state but not in the last state. ///  public bool IsNewPress(MouseButtons button) { switch (button) { case MouseButtons.LeftButton: return ( _lastMouseState.LeftButton == ButtonState.Released && _currentMouseState.LeftButton == ButtonState.Pressed); case MouseButtons.MiddleButton: return ( _lastMouseState.MiddleButton == ButtonState.Released && _currentMouseState.MiddleButton == ButtonState.Pressed); case MouseButtons.RightButton: return ( _lastMouseState.RightButton == ButtonState.Released && _currentMouseState.RightButton == ButtonState.Pressed); case MouseButtons.ExtraButton1: return ( _lastMouseState.XButton1 == ButtonState.Released && _currentMouseState.XButton1 == ButtonState.Pressed); case MouseButtons.ExtraButton2: return ( _lastMouseState.XButton2 == ButtonState.Released && _currentMouseState.XButton2 == ButtonState.Pressed); default: return false; } } ///  /// Checks if the requested mosue button is a current press. ///  ///  /// the mouse button to be checked. ///  ///  /// a bool indicating whether the selected mouse button is being /// pressed in the current state and in the last state. ///  public bool IsCurPress(MouseButtons button) { switch (button) { case MouseButtons.LeftButton: return ( _lastMouseState.LeftButton == ButtonState.Pressed && _currentMouseState.LeftButton == ButtonState.Pressed); case MouseButtons.MiddleButton: return ( _lastMouseState.MiddleButton == ButtonState.Pressed && _currentMouseState.MiddleButton == ButtonState.Pressed); case MouseButtons.RightButton: return ( _lastMouseState.RightButton == ButtonState.Pressed && _currentMouseState.RightButton == ButtonState.Pressed); case MouseButtons.ExtraButton1: return ( _lastMouseState.XButton1 == ButtonState.Pressed && _currentMouseState.XButton1 == ButtonState.Pressed); case MouseButtons.ExtraButton2: return ( _lastMouseState.XButton2 == ButtonState.Pressed && _currentMouseState.XButton2 == ButtonState.Pressed); default: return false; } } ///  /// Checks if the requested mosue button is an old press. ///  ///  /// the mouse button to check. ///  ///  /// a bool indicating whether the selected mouse button is not being /// pressed in the current state and is being pressed in the old state. ///  public bool IsOldPress(MouseButtons button) { switch (button) { case MouseButtons.LeftButton: return ( _lastMouseState.LeftButton == ButtonState.Pressed && _currentMouseState.LeftButton == ButtonState.Released); case MouseButtons.MiddleButton: return ( _lastMouseState.MiddleButton == ButtonState.Pressed && _currentMouseState.MiddleButton == ButtonState.Released); case MouseButtons.RightButton: return ( _lastMouseState.RightButton == ButtonState.Pressed && _currentMouseState.RightButton == ButtonState.Released); case MouseButtons.ExtraButton1: return ( _lastMouseState.XButton1 == ButtonState.Pressed && _currentMouseState.XButton1 == ButtonState.Released); case MouseButtons.ExtraButton2: return ( _lastMouseState.XButton2 == ButtonState.Pressed && _currentMouseState.XButton2 == ButtonState.Released); default: return false; } } #endif } } 

只需将其复制到一个单独的类fie中并将其移动到命名空间,然后声明一个(inputHelper变量),在initialiaze部分初始化它,并在更新逻辑之前在更新循环中调用inputHelper.Update()。 然后,只要您需要与输入相关的内容,只需使用InputHelper! 例如,在您的情况下,您将使用InputHelper.IsNewPress([输入按钮/键的类型])来检查是否要向下或向上移动菜单项。 对于此示例:inputHelper.IsNewPress(Keys.Down)

实现此目的的最佳方法是从刚刚传递的更新语句中缓存键盘/游戏手柄状态。

 KeyboardState oldState; ... var newState = Keyboard.GetState(); if (newState.IsKeyDown(Keys.Down) && !oldState.IsKeyDown(Keys.Down)) { // the player just pressed down } else if (newState.IsKeyDown(Keys.Down) && oldState.IsKeyDown(Keys.Down)) { // the player is holding the key down } else if (!newState.IsKeyDown(Keys.Down) && oldState.IsKeyDown(Keys.Down)) { // the player was holding the key down, but has just let it go } oldState = newState; 

在您的情况下,您可能只想在上面的第一种情况下“向下”移动,只需按下该键。

处理这种事情的一个好方法是为你感兴趣的每个键存储一个计数器,如果键是关闭的话你会增加每一帧,如果它关闭则重置为0。

这样做的好处是,您可以测试密钥的绝对状态(如果计数器非零,密钥已关闭),并且还可以轻松检查是否只是按下此框架的菜单等(计数器是1)。 加上键重复变得容易(计数器%重复延迟为零)。

如果你的应用程序是用于Windows机器,我使用这个在这里找到的事件驱动类取得了很大的成功: gamedev.net forum post

它可以处理重复启动前的典型按键和短暂停顿,就像Windows应用程序中的普通文本输入一样。 还包括鼠标移动/滚轮事件。

您可以订阅事件,例如,使用以下代码:

 InputSystem.KeyDown += new KeyEventHandler(KeyDownFunction); InputSystem.KeyUp += new KeyEventHandler(KeyUpFunction); 

然后在方法本身:

 void KeyDownFunction(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.F) facepalm(); } void KeyUpFunction(object sender, KeyEventArgs e) { if(e.KeyCode == Keys.F) release(); } 

……等等。 这真是一个很棒的课程。 我发现它的灵活性比XNA的默认键盘处理大大提高了。 祝好运!

我认为之前的答案有点过于复杂,所以我在这里给出了这个……

将KeyPress类复制到新文件中,声明KeyPress变量,在Initialize()方法中初始化它们。 从那里你可以做if ([yourkey].IsPressed()) ...

注意:此答案仅适用于键盘输入,但应轻松移植到游戏手柄或任何其他输入。 我认为保持不同类型输入的代码分离更好。

 public class KeyPress { public KeyPress(Keys Key) { key = Key; isHeld = false; } public bool IsPressed { get { return isPressed(); } } public static void Update() { state = Keyboard.GetState(); } private Keys key; private bool isHeld; private static KeyboardState state; private bool isPressed() { if (state.IsKeyDown(key)) { if (isHeld) return false; else { isHeld = true; return true; } } else { if (isHeld) isHeld = false; return false; } } } 

用法:

 // Declare variable KeyPress escape; // Initialize() escape = new KeyPress(Keys.Escape) // Update() KeyPress.Update(); if (escape.IsPressed()) ... 

我可能错了,但我认为我的答案在资源方面比接受的答案更容易,而且更具可读性!

您可以存储从最后一个按键(左,右……)的整数值时间,如果此时间大于某个限制,您可以轮询按下的新键。 但是这只能用于菜单,因为在游戏中你会立即需要这些信息。

你还可以做一个function,结合KyeUp和KeyDown,告诉你何时按下按键一次,只有一次更新循环,这样它只有在你再次按下按键时才有效。

好的,我已经弄清楚了。 首先,我添加了一个

 private Keys keyPressed = Keys.None; 

在我的Update()方法中,我执行以下操作:

  KeyboardState keyboardState = Keyboard.GetState(); if (keyboardState.IsKeyUp(keyPressed)) { keyPressed = Keys.None; } if (keyboardState.IsKeyDown(keyPressed)) { return; } // Some additionnal stuff is done according to direction if (keyboardState.IsKeyDown(Keys.Up)) { keyPressed = Keys.Up; } else if (keyboardState.IsKeyDown(Keys.Down)) { keyPressed = Keys.Down; } 

它似乎工作正常。

我保存了之前更新运行中的GamePadState和KeyboardState。 在下一次更新运行中,我检查上次运行时未按下的按钮,但现在按下。 然后我保存当前状态。

我把所有这些都包含在一个静态类中,我可以使用它来查询特定按钮和/或获取自上次更新以来按下的按钮列表。 这使得同时使用多个键非常容易(在游戏中你肯定想要的东西)并且它对于和弦来说可以轻松扩展。

拉涅利,这看起来像什么? 我正在努力处理这些更新周期……

Hrmmm …

 public static bool CheckKeyPress(Keys key) { return keyboardState.IsKeyUp(key) && lastKeyboardState.IsKeyDown(key); } 

SetStates()是私有的,它在Update()中调用

 private static void SetStates() { lastKeyboardState = keyboardState; keyboardState = Keyboard.GetState(); } 

这是更新……

 public sealed override void Update(GameTime gameTime) { // Called to set the states of the input devices SetStates(); base.Update(gameTime); } 

我试过添加额外的支票..

 if (Xin.CheckKeyPress(Keys.Enter) || Xin.CheckButtonPress(Buttons.A)) { if (Xin.LastKeyboardState != Xin.KeyboardState || Xin.LastGamePadState(PlayerIndex.One) != Xin.GamePadState(PlayerIndex.One)) { 

似乎没有任何明显的影响 – 我似乎无法减慢菜单确认,

那么你能做的就是这样(也会跟踪每一个键)

 int[] keyVals; TimeSpan pressWait = new TimeSpan(0, 0, 1); Dictionary keyDowns = new Dictionary(); Dictionary keyTimes = new Dictionary(); public ConstructorNameHere { keyVals = Enum.GetValues(typeof(Keys)) as int[]; foreach (int k in keyVals) { keyDowns.Add((Keys)k, false); keyTimes.Add((Keys)k, new DateTime()+ new TimeSpan(1,0,0)); } } protected override void Update(GameTime gameTime) { foreach (int i in keyVals) { Keys key = (Keys)i; switch (key) { case Keys.Enter: keyTimes[key] = (Keyboard.GetState().IsKeyUp(key)) ? ((keyDowns[key]) ? DateTime.Now + pressWait : keyTimes[key]) : keyTimes[key]; keyDowns[key] = (keyTimes[key] > DateTime.Now) ? false : Keyboard.GetState().IsKeyDown(key); if (keyTimes[key] < DateTime.Now) { // Code for what happens when Keys.Enter is pressed goes here. } break; } } 

通过这种方式,您可以检查每个键。 您也可以通过创建单独的DateTimes和单独的bool值来为每个键执行此操作。

我知道这是旧的,但如何:添加线程安全字典:

 private ConcurrentDictionary _keyBounceDict = new ConcurrentDictionary(); 

然后使用此方法跟踪按下的键并确定是否存在键反弹:

  /////////////////////////////////////////////////////////////////////////////////////////// /// IsNotKeyBounce - determines if a key is bouncing and therefore not valid within /// a certain "delay" period /////////////////////////////////////////////////////////////////////////////////////////// private bool IsNotKeyBounce(Keys thekey, double delay) { bool OKtoPress = true; if (_keyBounceDict.ContainsKey(thekey)) { TimeSpan ts = DateTime.Now - _keyBounceDict[thekey]; if (ts.TotalMilliseconds < _tsKeyBounceTiming) { OKtoPress = false; } else { DateTime dummy; _keyBounceDict.TryRemove(thekey, out dummy); } } else { _keyBounceDict.AddOrUpdate(thekey, DateTime.Now, (key, oldValue) => oldValue); } return OKtoPress; } 

这是我在Update方法中添加的内容:

  if (Keyboard.GetState().IsKeyDown(Keys.W)) { if (IsNotKeyBounce(Keys.W, 50.0)) _targetNew.Distance *= 1.1f; } 

我使用50毫秒,但你可以使用任何有意义的应用程序或绑定到GameTime或其他任何…