2010-03-24 11 views
13

Escribí un componente para mostrar el FPS actual.
La parte más importante de la misma es:¿Cómo calcular correctamente FPS en XNA?

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); 
    } 

En la mayoría de los casos funciona bien, pero recientemente he tenido un problema.
Cuando pongo el siguiente código en el método de actualización del juego, algo extraño comienza a suceder.

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

La idea principal de este código es ejecutar el algoritmo PathFinding en diferentes hilos todo el tiempo.

Por cosas extrañas quiero decir que a veces el FPS disminuye drásticamente, esto es obvio, pero el FPS mostrado cambia más de una vez por segundo. Si entiendo este código, FPS no puede cambiar más de una vez por segundo.

¿Alguien me puede explicar lo que está pasando?

Editar 26.03.2010
que he publicado también el código del método Draw.

Respuestas Editar 31.03.2010 a las preguntas Venesectrix
1) usted está funcionando con un intervalo de tiempo fijo o variable?
IsFixedTimeStep y SynchronizeWithVerticalRetrace se establece en verdadero.
2) ¿Estaba moviendo la ventana XNA cuando esto ocurrió?
No
3) ¿La ventana XNA tiene foco?
4) ¿Qué tan notable fue (es decir, actualizando tan rápido que no se puede leer, o simplemente se actualiza apenas más de un segundo)?
Pude leer las actualizaciones, el FPS se actualizaba ~ 3 veces por segundo.
5) ¿Y todo esto solo ocurre con el código de la secuencia ahí?

+1

Una pequeña corrección, si (elapseTime> = 1) {FPS = frameCount/elapseTime; elapseTime = 0;} – Martin

+0

Ah, y obviamente establezca framecount en cero – Martin

+0

@Martin: Por supuesto, tiene razón, aumentará la precisión, pero no resolverá mi problema. Gracias de cualquier manera. –

Respuesta

12

Shawn Hargreaves tiene un gran post sobre este here. La primera diferencia que veo entre su código y el tuyo es el hecho de que restableces tu elapseTime a 0 cada vez, lo que perderá algo de tiempo, mientras que Shawn solo resta 1 segundo de su tiempo transcurrido. Además, Shawn usa ElapsedGameTime en lugar de ElapsedRealTime. También actualiza su frameCounter en la función Draw en lugar de la función Update.

En cuanto a por qué se utiliza ElapsedRealTime, lo explica en un comentario después del mensaje:

> Seguramente 1/gameTime.ElapsedRealTime.TotalSeconds

> por lo tanto, se dará la tasa de fotogramas actual.

que le dirá cuánto tiempo pasó desde la llamada anterior para actualizar, pero que no es lo mismo que su de imágenes por segundo!

a) Si el juego está soltando marcos, se llamará a la actualización con más frecuencia para ponerse al día. Desea cronometrar el número de sorteos reales que están teniendo lugar, no solo estos marcos lógicos de puesta al día extra.

b) El tiempo para una sola actualización puede fluctuar ampliamente, por lo que la figura que salir de ese será demasiado flickery para ser fácilmente legible.

Me gustaría probar su componente, y ver si funciona para usted. La publicación es bastante antigua, y creo que tendrás que cambiar LoadGraphicsContent por LoadContent y UnloadGraphicsContent por UnloadContent, como señala otro de los comentarios.

+0

@Venesectrix: Gracias, cambiando de ElapsedRealTime a ElapsedGameTime resolvió este problema, pero la pregunta por qué se mostraba FPS cambiaba más de una vez por segundo todavía está abierto. –

+0

¿Puedes publicar el código para tu función de dibujo también? – Venesectrix

+0

Lo publico, pero no hay nada interesante. –

1

Como nota aparte ... debe evitar establecer la prioridad del subproceso. Al asignar la prioridad de subproceso más alta a lo que debería ser un subproceso de fondo, podría terminar muriendo de hambre en el subproceso principal de la CPU porque el planificador daría prioridad a threadPath

+0

@Joel Martinez: En la versión normal este hilo no tendrá la más alta prioridad. Estaba comprobando cuánto disminuiría mi FPS si cambio la prioridad. –

0

¿Está comprobando activamente si IsRunningSlowly está siendo modificado? Incluso si IsFixedTimeStep es verdadero, si su programa no puede hacer tantas Actualizaciones como espera, lo llamará con más frecuencia.

Una manera que he mitigado esto antes es llamando directamente a ResetElapsedTime() en lugar de seguirlo usted mismo.

Sin embargo, no estoy seguro si eso le sirve. Me di cuenta de que cuando estaba depurando el problema anterior que tenía, no llamaría a las actualizaciones adicionales, probablemente una 'característica' al depurar.

2

Aquí es cómo lo hago y con este método:

  • Usted promedio durante N tramas
  • Usted puede utilizarlo con cualquier método de inicialización a elegir
  • Debe ser fácil de leer y seguir
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); 
    } 
} 
} 

cuanto a: "pero la pregunta de por qué aparece FPS estaba cambiando más a menudo t una vez por segundo sigue abierto "

La diferencia entre ElapsedGameTime y ElapsedRealTime es que" ElapsedGameTime "es la cantidad de tiempo desde la última vez que ingresó la instrucción Update o Draw (según el" gameTime "que esté usando - el de Update o el de Draw).

ElapsedRealTime es el tiempo desde que comenzó el juego. Debido a esto, aumenta linealmente a medida que el juego continúa funcionando. De hecho, después de 1 segundo, podrás actualizar cada fotograma debido a que su lógica era la siguiente:

(Vamos a suponer que se estaba ejecutando 4 fps en aras de fácil explicación):

  • fotograma 1: ElapsedRealTime: 0.25. Total acumulado ahora: 0.25
  • Marco 2: ElapsedRealTime: 0.5 Total acumulado ahora: 0.75
  • Recuadro 3: ElapsedRealTime: 0.75 Total acumulado ahora: 1.5
  • Correr total mayor que 1 !!! ¡Mostrar fps!
  • conjunto de ejecución Total = 0
  • Frame 4: ElapsedRealTime: 1.00 total acumulado ahora: 1,0
  • corriente total superior a 1 !!! ¡Mostrar fps!

Ahora que ha arreglado el contador, ahora debería estar recibiendo solamente cambios transcurrido Tiempo de juego de un modo constante la progresión 0.25 ahora se mueve:

  • Frame 1: ElapsedGameTime: 0,25. total acumulado ahora: 0,25
  • Frame 2: ElapsedGameTime: 0.25 total acumulado ahora: 0,50
  • Frame 3: ElapsedGameTime: 0.25 total acumulado ahora: 0,75
  • Frame 4: ElapsedGameTime: 0.25 total acumulado ahora: 1,00
  • Corriendo total mayor que 1 !!! ¡Mostrar fps!
  • conjunto de ejecución total de Frame = 0

  • 5: ElapsedGameTime: 0,25. total acumulado ahora: 0,25

  • Frame 6: ElapsedGameTime: 0.25 total acumulado ahora: 0,50
  • Frame 7: ElapsedGameTime: 0.25 total acumulado ahora: 0,75
  • Frame 8: ElapsedGameTime: 0.25 total acumulado ahora: 1,00
  • Corriendo total mayor que 1 !!! ¡Mostrar fps!
  • Conjunto total acumulado = 0

¿Qué es lo que está esperando. En resumen, ahora que corrigió el primer problema, debería haber corregido el segundo también y "por qué" se explicó anteriormente.

0

Usted debe trabajar en el mostrador fps en el método de sorteo

0

Hola chicos si desea mostrar su verdadera velocidad de fotogramas que tiene que aplicar contador de imágenes por segundo con el método Draw, porque XNA hacerlo así: "Si el equipo no puede servir el método Update suspenda Draw método y en lugar de que sirva el método Update"