我的游戏中的垂直菜单允许您向上或向下按下拇指操纵杆以跳转到下一个(或上一个)菜单项。我通过存储旧的拇指操纵杆状态来实现这一点。如果状态为零且现在为非零,则选择下一个(或上一个)菜单项。
我从来没有注意到在连接到PC或Xbox的Xbox控制器上使用此实现的任何“轻弹”。我已经测试了几个控制器。
但是,当我回答问题XNA - how to tell if a thumb stick was “twitched” in a certain direction时,Andrew Russell发表了评论:
虽然这是正确的想法,但是 阈值需要高于零(对于 在菜单中使用),需要有一个 激活和之间的差距 停用以防止它轻弹。
我的理解是Xna已经在拇指杆输入上提供过滤和“死区”,并且不需要阈值或余量来防止轻弹。安德鲁的声明让我很担心。
是否有控制器需要阈值或保证金以防止“轻弹”?
为了安全,我应该在阈值和边距内进行编码吗?
以下代码是我的XNA菜单输入轮询方法的最小实现。唯一的区别是我的实际菜单在控件的值在相同方向上短时间内非零时自动滚动。与此问题相关的逻辑位于ProcessUserInput函数中。
球代表活动菜单项。向上或向下按左拇指操纵杆将球跳到下一个/上一个菜单项。我不能让它“闪烁”。
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace WindowsGame
{
public class Ball
{
public float RADIUS = DIAMETER * 0.5f;
const int DIAMETER = 40;
static readonly uint WHITE = Color.White.PackedValue;
static readonly uint BLACK = new Color(0, 0, 0, 0).PackedValue;
Texture2D m_texture;
public Ball(GraphicsDevice graphicsDevice)
{
m_texture = new Texture2D(graphicsDevice, DIAMETER, DIAMETER);
uint[] data = new uint[DIAMETER * DIAMETER];
for (int i = 0; i < DIAMETER; i++)
{
float iPosition = i - RADIUS;
for (int j = 0; j < DIAMETER; j++)
{
data[i * DIAMETER + j] = new Vector2(iPosition, j - RADIUS).Length() <= RADIUS ? WHITE : BLACK;
}
}
m_texture.SetData<uint>(data);
}
public Vector2 Position { get; set; }
private Rectangle DrawRectangle
{
get
{
return new Rectangle((int)Math.Round(Position.X - RADIUS), (int)Math.Round(Position.Y - RADIUS), DIAMETER, DIAMETER);
}
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(m_texture, DrawRectangle, Color.White);
}
}
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Matrix viewMatrix;
Matrix inverseViewMatrix;
Ball ball;
GamePadState m_lastGamePadState;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void Initialize()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
ball = new Ball(GraphicsDevice);
viewMatrix = Matrix.CreateTranslation(Window.ClientBounds.Width * 0.5f, Window.ClientBounds.Height * 0.5f, 0.0f);
inverseViewMatrix = Matrix.Invert(viewMatrix);
m_lastGamePadState = GamePad.GetState(PlayerIndex.One);
base.Initialize();
}
private void ProcessUserInput()
{
GamePadState gamePadState = GamePad.GetState(PlayerIndex.One);
if (m_lastGamePadState.ThumbSticks.Left.Y == 0.0f)
{
ball.Position += Vector2.UnitY * (-Math.Sign(gamePadState.ThumbSticks.Left.Y) * ball.RADIUS * 2.0f);
}
m_lastGamePadState = gamePadState;
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
ProcessUserInput();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, viewMatrix);
ball.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
答案 0 :(得分:1)
你可能想要
GamePad.GetState(PlayerIndex.One, GamePadDeadZone.IndependentAxes);
这应该就是你所需要的。见GamePadDeadZone。 (编辑:刚刚注意到这是默认的 - 你应该已经没事了。)
就个人而言,我使用Buttons.LeftThumbstickUp等进行GUI导航,这样您就可以像Dpad一样对待拇指操纵杆。 p>
答案 1 :(得分:1)
它基本上是让控件感觉良好&#34;很好&#34;。
需要将阈值设置为零以上(因为棒子永远不会完全为零)。死区计为阈值,但它非常小。我认为默认的XNA死区约为0.24。这太小了,无法在菜单中使用。
我怀疑 - 但我现在无法对此进行测试--XNA的解释 - 拇指按钮代码使用相同的,微小的,不满意的死区域作为其激活阈值。
从您链接的答案中可以看出,我将激活阈值设置为0.85,这相当高。不可否认,我的代码不适用于菜单。菜单的调整可能会稍微小一点,但仍然不如默认的死区小。
至于保证金(例如:在0.85激活,在0.75激活)。当您特别回应&#34; off-on-off&#34;时,尤其需要这样才能获得满意的感觉。过渡。当您只是简单地检测到&#34; on&#34;时,这并不是必需的。状态。
它还可以防止您按下按钮时按下按钮的情况(就像菜单一样),并且在极少数情况下可能会受到虚假的双击。