2010-03-24 60 views
13

我写了一个组件来显示当前的FPS。
它的最重要的部分是:如何在XNA中正确计算FPS?

public override void Update(GameTime gameTime) 
    { 
     elapseTime += (float)gameTime.ElapsedRealTime.TotalSeconds; 
     frameCounter++; 

     if (elapseTime > 1) 
     { 
      FPS = frameCounter; 
      frameCounter = 0; 
      elapseTime = 0; 
     } 
     base.Update(gameTime); 
    } 


    public override void Draw(GameTime gameTime) 
    { 
     spriteBatch.Begin(); 

     spriteBatch.DrawString(font, "FPS " + ((int)FPS).ToString(), position, color, 0, origin, scale, SpriteEffects.None, 0); 

     spriteBatch.End(); 

     base.Draw(gameTime); 
    } 

在大多数情况下,它工作正常,但最近我有一个问题。
当我将下面的代码放入游戏的更新方法时,奇怪的事情开始发生。这段代码的

 if (threadPath == null || threadPath.ThreadState != ThreadState.Running) 
     { 
      ThreadStart ts = new ThreadStart(current.PathFinder.FindPaths); 
      threadPath = new Thread(ts); 
      threadPath.Priority = ThreadPriority.Highest; 
      threadPath.Start(); 
     } 

主要思想是在不同的线程中运行寻路算法所有的时间。

奇怪的事情我的意思是,有时FPS剧烈下降,这是显而易见的,但显示FPS的变化比每秒一次更频繁。如果我理解这个代码,FPS不可能每秒更换一次。

有人能解释我发生了什么事吗?

编辑26.03.2010
我已经发布了Draw方法的代码。

编辑31.03.2010答案Venesectrix问题
1)给你一个固定或可变的时间步运行?
IsFixedTimeStep和SynchronizeWithVerticalRetrace设置为true。
2)发生这种情况时,您是否在移动XNA窗口?

3)XNA窗口是否有焦点?

4)它有多明显?(即更新速度如此之快以至于无法读取,或者只是几乎没有更新)?
我能够读取更新,FPS每秒更新〜3次。
5)所有这一切只发生在那里的线程代码?
是的

+1

。 elapseTime = 0;} – Martin 2010-03-24 23:03:08

+0

哦,并明显将framecount设置为零 – Martin 2010-03-24 23:08:45

+0

@Martin:当然你是对的,它会提高精度,但它不会解决我的问题。不管怎么说,还是要谢谢你。 – 2010-03-24 23:09:22

回答

12

肖恩哈格里夫斯有一个伟大的职位关于这个here。我在他的代码和你的代码之间所看到的第一个区别是,你每次都将你的elapseTime重置为0,这会失去一些时间,而Shawn只是从他的elapsedTime中减去1秒。另外,Shawn使用ElapsedGameTime而不是ElapsedRealTime。他在Draw函数中更新了他的frameCounter,而不是Update函数。

至于他为什么使用ElapsedRealTime,他解释了它在帖子后留言:

>当然1/gameTime.ElapsedRealTime.TotalSeconds

>将因此给出当前帧率。

这会告诉你自上次致电更新后 多久,但 与您的 帧率不同!

a)如果游戏正在丢帧, 更新将更频繁地调用 以赶上。你想要时间 实际抽签的数量是 发生,而不仅仅是这些额外的 赶上逻辑框架。

B)的时间为一个单一的更新可以 波动很大,所以你 摆脱这一数字将太flickery 以易于阅读。

我会尝试他的组件,看看它是否适合你。这篇文章很老了,我想你将不得不将LoadGraphicsContent改为LoadContent和UnloadGraphicsContent为UnloadContent,作为另一个评论指出。

+0

@Venesectrix:谢谢,从ElapsedRealTime更改为ElapsedGameTime解决了这个问题,但问题为什么显示的FPS更改的频率比每秒一次仍然开放。 – 2010-03-25 09:55:30

+0

你可以发布你的绘图函数的代码吗? – Venesectrix 2010-03-25 17:41:38

+0

我发布了它,但没有什么有趣的。 – 2010-03-25 23:14:52

1

另外...你应该避免设置线程优先级。通过将最高线程优先级分配给后台线程,您可能最终导致CPU时间主线程饿死,因为调度程序会优先考虑threadPath

+0

@Joel Martinez:在正常版本中,该线程不会具有最高优先级。我只是检查如果我改变优先级,我的FPS会减少多少。 – 2010-03-25 09:57:38

0

您是否正在检查IsRunningSlowly是否正在更改?即使IsFixedTimeStep为true,如果您的程序无法执行尽可能多的更新,它会更频繁地调用它。

我之前减轻过的一种方法是直接调用ResetElapsedTime(),而不是自己跟踪它。

不知道这是否适合你。我注意到,当我调试我以前的问题时,它不会调用额外的更新,在调试时可能是一个'功能'。

2

这是我如何做到这一点,用这种方法:

  • 您平均值N帧
  • 你可以使用任何初始化方法,使用它你选择
  • 它应该很容易阅读和遵守
using System; 
using System.Collections.Generic; 
using System.Linq; 
using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Audio; 
using Microsoft.Xna.Framework.Content; 
using Microsoft.Xna.Framework.GamerServices; 
using Microsoft.Xna.Framework.Graphics; 
using Microsoft.Xna.Framework.Input; 
using Microsoft.Xna.Framework.Media; 

namespace _60fps 
{ 
public class Game1 : Microsoft.Xna.Framework.Game 
{ 
    GraphicsDeviceManager graphics; 
    SpriteBatch spriteBatch; 
    SpriteFont OutputFont; 
    float Fps = 0f; 
    private const int NumberSamples = 50; //Update fps timer based on this number of samples 
    int[] Samples = new int[NumberSamples]; 
    int CurrentSample = 0; 
    int TicksAggregate = 0; 
    int SecondSinceStart = 0; 

    public Game1() 
    { 
     graphics = new GraphicsDeviceManager(this); 
     Content.RootDirectory = "Content"; 
    } 

    protected override void Initialize() 
    { 
     base.Initialize(); 
     graphics.SynchronizeWithVerticalRetrace = false; 
     int DesiredFrameRate = 60; 
     TargetElapsedTime = new TimeSpan(TimeSpan.TicksPerSecond/DesiredFrameRate); 
    } 

    protected override void LoadContent() 
    { 
     spriteBatch = new SpriteBatch(GraphicsDevice); 
     OutputFont = Content.Load<SpriteFont>("MessageFont"); 
    } 

    protected override void UnloadContent() 
    {/* Nothing to do */} 

    protected override void Update(GameTime gameTime) 
    { 
     if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape)) 
      this.Exit(); 

     base.Update(gameTime); 
    } 

    private float Sum(int[] Samples) 
    { 
     float RetVal = 0f; 
     for (int i = 0; i < Samples.Length; i++) 
     { 
      RetVal += (float)Samples[i]; 
     } 
     return RetVal; 
    } 

    private Color ClearColor = Color.FromNonPremultiplied(20, 20, 40, 255); 
    protected override void Draw(GameTime gameTime) 
    { 
     Samples[CurrentSample++] = (int)gameTime.ElapsedGameTime.Ticks; 
     TicksAggregate += (int)gameTime.ElapsedGameTime.Ticks; 
     if (TicksAggregate > TimeSpan.TicksPerSecond) 
     { 
      TicksAggregate -= (int)TimeSpan.TicksPerSecond; 
      SecondSinceStart += 1; 
     } 
     if (CurrentSample == NumberSamples) //We are past the end of the array since the array is 0-based and NumberSamples is 1-based 
     { 
      float AverageFrameTime = Sum(Samples)/NumberSamples; 
      Fps = TimeSpan.TicksPerSecond/AverageFrameTime; 
      CurrentSample = 0; 
     } 

     GraphicsDevice.Clear(ClearColor); 
     spriteBatch.Begin(); 
     if (Fps > 0) 
     { 
      spriteBatch.DrawString(OutputFont, string.Format("Current FPS: {0}\r\nTime since startup: {1}", Fps.ToString("000"), TimeSpan.FromSeconds(SecondSinceStart).ToString()), new Vector2(10,10), Color.White); 
     } 
     spriteBatch.End(); 
     base.Draw(gameTime); 
    } 
} 
} 

至于: “但为什么显示FPS的问题发生了变化往往ŧ一秒钟仍然打开“

ElapsedGameTime和ElapsedRealTime之间的差异是”ElapsedGameTime“是自上次输入更新或绘图语句以来的时间量(具体取决于您使用的是哪个”gameTime“ - 来自Update的或来自Draw的那个)。

ElapsedRealTime是游戏开始以来的时间。正因为如此,随着游戏的继续运行,它会线性增加。事实上,在1秒钟后,你会因为你的逻辑看起来像这样更新每一帧:

(假设你正在运行4个FPS为便于说明):

  • 的第1帧:ElapsedRealTime: 0.25。现在运行总数:0.25
  • 第2帧:ElapsedRealTime:0.5现在运行总数:0.75
  • 第3帧:ElapsedRealTime:0.75现在运行总数:1。5
  • 运行总数大于1 !!!显示FPS!
  • 集运行总计= 0
  • 帧4:ElapsedRealTime:1.00现在运行总计:1.0
  • 运行总大于1!显示FPS!

现在你已经固定的柜台,你应该现在只能是越来越稳定的0.25消逝游戏时间的变化,因此进展现在移动:

  • 的第1帧:ElapsedGameTime:0.25。现在运行总计:0.25
  • 2帧:ElapsedGameTime:0.25现在运行总计:0.50
  • 帧3:ElapsedGameTime:0.25运行总数现在:0.75
  • 帧4:ElapsedGameTime:0.25运行总数现在:1.00
  • 运行总数大于1!显示FPS!
  • 集运行总计= 0

  • 框架5:ElapsedGameTime:0.25。现在运行总计:0.25

  • 框架6:ElapsedGameTime:0.25现在运行总计:0.50
  • 框架7:ElapsedGameTime:0.25运行总数现在:0.75
  • 框架8:ElapsedGameTime:0.25运行总数现在:1.00
  • 运行总数大于1!显示FPS!
  • 集运行总计= 0

哪个是你期待什么。总之,现在你已经纠正了第一个问题,你应该已经纠正了第二个问题,上面解释了“为什么”。

0

如果你想显示自己的真实帧率您必须实现帧率柜台方法Draw,因为XNA像这样做你应该draw方法下运行的FPS计数器

0

嗨,大家好: “如果你的电脑如果(elapseTime> = 1){FPS = FrameCount/elapseTime;如果(elapseTime> = 1),则不能提供方法Update方法Draw方法而不是服务Update方法“