在 XNA 应用程序中移动单个精灵时出现卡顿

本文关键字:精灵 单个 XNA 应用程序 移动 | 更新日期: 2023-09-27 18:30:34

>更新:我在这里上传了一个显示口吃的视频: http://intninety.co.uk/xnastutter.mp4 如果您不以 1920x1080 观看视频,您可能需要仔细查看视频,但您会发现每 2 秒左右移动一次时有相当明显的卡顿,我建议您在 Windows Media Player 而不是您的 Web 浏览器中查看它,以确保视频本身不会断断续续,从而防止您看到实际的口吃

最近正在拿起我不久前开始的一个项目,但我仍在努力解决我留下的问题!

目前,我有一个非常简单的应用程序,屏幕上只有一个精灵,并使用方向键四处移动。问题是每隔两秒左右,游戏就会卡顿,精灵似乎会向后跳跃,然后又会非常快地向前跳。

子画面本身是一个 55x33 的位图,所以不是什么大的东西,使用的代码如下。希望这足以让一些关于可能出现问题的想法滚动,如果需要一个视频来准确查看口吃的外观,我可以将一个放在一起并在需要时将其上传到某个地方。

正如您将在代码中看到的那样,它确实通过使运动发生时更大来补偿帧之间的时间损失,但是这种下降在时间上非常一致地发生,这让我相信我在某处做错了什么。

我已经在几台不同的机器上尝试过,但问题在所有机器上都仍然存在,如果有人有任何想法或可以看到我搞砸了它的位置,如果您能指出来,将不胜感激。

谢谢:)

游戏的构造函数 设置图形设备管理器

graphics = new GraphicsDeviceManager(this);
graphics.IsFullScreen = true;
graphics.SynchronizeWithVerticalRetrace = false;
graphics.PreferredBackBufferWidth = 1920;
graphics.PreferredBackBufferHeight = 1080;
Content.RootDirectory = "Content";
this.IsFixedTimeStep = false;

游戏更新方法中的代码

KeyboardState keyboard = Keyboard.GetState();
GamePadState gamePad = GamePad.GetState(PlayerIndex.One);
if (keyboard.IsKeyDown(Keys.Escape)) {
    this.Exit();
}
if ((keyboard.IsKeyDown(Keys.Left)) || (gamePad.DPad.Left == ButtonState.Pressed))
{
    this.player.MoveLeft((float)gameTime.ElapsedGameTime.TotalMilliseconds); 
} else if ((keyboard.IsKeyDown(Keys.Right)) || (gamePad.DPad.Right == ButtonState.Pressed))
{
    this.player.MoveRight((float)gameTime.ElapsedGameTime.TotalMilliseconds);
}
if ((keyboard.IsKeyDown(Keys.Up)) || (gamePad.DPad.Up == ButtonState.Pressed))
{
    this.player.MoveUp((float)gameTime.ElapsedGameTime.TotalMilliseconds);
} else if ((keyboard.IsKeyDown(Keys.Down)) || (gamePad.DPad.Down == ButtonState.Pressed))
{
    this.player.MoveDown((float)gameTime.ElapsedGameTime.TotalMilliseconds);
}
base.Update(gameTime);

上述更新方法中看到的"移动"方法

public void MoveLeft(float moveBy)
{
    this.position.X -= (moveBy * this.velocity.X);
}
public void MoveRight(float moveBy)
{
    this.position.X += (moveBy * this.velocity.X);
}
public void MoveUp(float moveBy)
{
    this.position.Y -= (moveBy * this.velocity.Y);
}
public void MoveDown(float moveBy)
{
    this.position.Y += (moveBy * this.velocity.Y);
}

游戏的抽奖方法

GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
spriteBatch.Draw(this.player.Texture, this.player.Position, null, Color.White, this.player.Rotation, this.player.Origin, 1.0f, SpriteEffects.None, 0.0f);
spriteBatch.End();
base.Draw(gameTime);

编辑:忘了提,Move 方法中使用的速度对象是 Vector2

在 XNA 应用程序中移动单个精灵时出现卡顿

我已经设法看到它在一瞬间发生一次,这让我想到了我认为的问题所在。由于您使用原始ElapsedGameTime.TotalMilliseconds值作为运动的因素,因此程序经历的所有计算机滞后都将直接应用于运动。例如,如果您的计算机 (OS) 在二十分之一秒内执行其他操作,则经过的时间值将累积到 ~50 毫秒的值,而通常约为 0.3 毫秒。这将导致帧的运动量是正常帧的 150 倍。

要手动执行此操作,您可以执行以下操作:

// define a frame counter
private int mCounter;
...
protected override void Update(GameTime pGameTime)
{
   // save the elapsed time value
   float time = (float)pGameTime.ElapsedGameTime.TotalMilliseconds;
   ...
   // force a long frame every 2500th frame (change depending on your framerate)
   if (mCounter++ > 2500)
   {
      mCounter = 0;
      time = 75; // about 225 times longer frame
   }
   ...
   // use the time value in your move calls
   if ((keyboard.IsKeyDown(Keys.Left)) || (gamePad.DPad.Left == ButtonState.Pressed))
      mPlayer.MoveLeft(time);

为了防止这种情况发生(除了设置IsFixedTimeStep = true;,这会立即修复它;但假设您希望IsFixedTimeStep false),您应该使用时间值,如上所述,但限制它。由您决定经过的时间与运动的比例,并确定每帧允许通过的好时间量。前任:

protected override void Update(GameTime pGameTime)
{
   // save the elapsed time value
   float time = (float)pGameTime.ElapsedGameTime.TotalMilliseconds;
   if (time > 1)
      time = 1;
   ...
   if ((keyboard.IsKeyDown(Keys.Left)) || (gamePad.DPad.Left == ButtonState.Pressed))
      mPlayer.MoveLeft(time);
   ...

虽然这将纠正您当前程序的问题,但由于没有发生很多事情,因此您的每帧帧只有 0.3 毫秒。一旦您的游戏有更多内容,每帧将花费更多时间,并且您希望上限远高于 1 毫秒。

编辑:需要明确的是,这是CPU/OS的"停机时间"。它不会消失*,只是取决于你是否在它发生时跳到一堆(例如,如果经过的时间达到 2000 毫秒,这可能会导致问题),或者限制这些峰值并让它们导致滞后;无论哪种方式,你的运动中都会有一个你无法填补的"洞"。这确实很正常,并不像看起来那么重要。一旦你的游戏中发生了更多的事情,它就会变得越来越不引人注目。它目前非常突出,特别是因为只有两个图形存在,没有其他事情发生。

*(实际上,您可能会寻找可以关闭的其他应用程序和进程,以防止CPU被其他程序借用,但是由于您使用的是多任务操作系统,因此永远无法保证自己拥有CPU。

再次更新

只是有一个想法。你有没有检查过(float)gameTime.ElapsedGameTime.TotalMilliseconds不会导致无穷大或负数?经过的时间变为负值可以解释你的精灵向后跳跃然后向前跳跃。

是键盘。您可以通过将鼠标左右移动更改为鼠标左键和鼠标右键来证明这一点。 使用键下和键关闭触发器设置状态,而不是按键关闭。

更新:

似乎有一些事情会导致 XNA 中的"口吃"。我以为你的问题肯定是键盘问题。但是,我只能假设您也使用GamePad测试了您的代码,并且它遭受了相同的影响。

IsFixedTimeStep还有另一个问题,但您已经将其设置为 false。快速的谷歌搜索表明,有很多人遇到这些类型的问题,没有任何明确的解决方案。

在 http://forums.create.msdn.com/forums/t/9934.aspx?PageIndex=6 有一个很长的讨论

其他注意事项:

尝试将帧速率限制为 60 fps。无论如何,没有任何理由超越这一点。一项功能表明,不执行任何操作的 2D 应用可能会以显著较高的吞吐量停滞图形管道。

检查是否曾经调用过 IsSlowRun。垃圾回收和混沌理论可能会导致此设置。我