6

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

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

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

精灵本身是一个 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

4

2 回答 2

1

我设法看到它在一瞬间发生了一次,这导致了我认为是问题所在。由于您将原始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给自己。)

于 2012-04-11T13:27:29.807 回答
0

再次更新

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

它的键盘。您可以通过将左右移动更改为鼠标左键和鼠标右键单击来证明这一点。使用 keydown 和 keyup 触发器来设置状态而不是 iskeydown。

更新:

似乎有几件事会导致 XNA 中出现“卡顿”。我认为您的问题肯定是键盘问题。但是,我只能假设您已经使用 GamePad 测试了您的代码并且它也遭受了同样的影响。

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

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

其他要看的东西:

尝试将帧速率限制为 60 fps。无论如何,没有任何理由超越这一点。一种力量表明,一个不做任何事情的 2d 应用程序可能会以非常高的吞吐量停止图形管道。

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

于 2012-04-10T23:04:57.217 回答