如何减慢或停止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或其他任何…