在XNA应用程序中移动单个sprite时出现断断续续的情况

时间:2012-04-10 19:39:47

标签: c# xna sprite

更新:我上传的视频显示了这里的口吃: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

2 个答案:

答案 0 :(得分:2)

我已经设法看到它一瞬间发生一次,这让我想到了我认为的问题。由于您使用原始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;,这会立即修复它;但假设您希望IsFixedTimeStepfalse),您应该使用时间值,如上所述,但限制它。由您决定运动所用时间的比例,并确定允许每帧传递的时间量。例如:

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毫秒。一旦你的游戏有更多的东西,每帧将有更多的时间流逝,你将希望上限远高于1ms。

编辑: 要清楚,这是CPU /操作系统的“停机时间”。它不会消失*,它取决于你是否在它发生时跳过一堆(例如,如果经过的时间使其达到2000毫秒就会引起问题),或者限制这些峰值并让它们引起落后;无论哪种方式,你的动作中都会有一个“洞”,你无法填补。这确实很正常,并不像看起来那么重要。一旦你的游戏中发生了更多的事情,它就会变得越来越不明显。它在当下非常突出,特别是因为只有两个图形存在且没有其他任何事情发生。

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

答案 1 :(得分:1)

再次更新

刚想了一想。你有没有检查(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。垃圾收集和混沌理论可能会导致这种情况发生。我