Wednesday, September 13, 2006

FRAMERATE COMPONENT FOR XNA

Just a quick note: I have posted the source code for a simple component that calculates and display the framerate of your XNA-game project.

Considerations:

  • You might change the namespace in line to your game's namespace.
  • Currently, the game window's title will be used to display the framerate (this will change when XNA supports fonts). As a workaround, you can use garykac's bitmap fonts example to modify this behavior.
  • If "Enabled" is set to false, framerates are not calculated (Current is set to 0).
  • If "Visible" is set to false, the current framerate value is not displayed (but yet it can be calculated if enabled).
  • When you want to show the decimal part of the framerate value (that is, "ShowDecimals" is set to true), you can set the display format to the fixed one (like "0.00").
  • You can also set whether the time step must be fixed or not.
  • You are free to modify/optimize the source code as desired. Please share your optimizations with the community.

Enjoy!!!

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Components;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;

namespace WindowsGame1
{
/// <summary>
/// Simple class to calculate the frame-per-seconds rate of your game.
/// </summary>
public sealed partial class Framerate : GameComponent
{
#region Instance Fields

private float deltaFPSTime;
private double currentFramerate;
private string windowTitle, displayFormat;
private bool enabled, visible, canDraw, showDecimals;

#endregion

#region Instance Properties

/// <summary>
/// Gets the current framerate.
/// </summary>
/// <remarks>
/// The 'Enabled' property must have been set to true to retrieve values greater than zero.
/// </remarks>
public double Current
{
get{ return this.currentFramerate; }

}

/// <summary>
///
Gets or Sets a value to enable framerate calculation.
/// </summary>
public bool
Enabled
{
get { return this.enabled; }
set
{
this.enabled = value;
this
.currentFramerate = 0;

if (this.Game != null && this.windowTitle != null && !this
.enabled)
this.Game.Window.Title = this.windowTitle;
}
}

/// <summary>
///
Gets or Sets a value to display framerate on screen.
/// </summary>
/// <remarks>
///
Currently, the framerate is shown in the window's title of the game.
/// </remarks>
public bool Visible{

get { return this.visible; }
set
{
this.visible = value;

if (this.Game != null && this.windowTitle != null && !this
.visible)
this.Game.Window.Title = this.windowTitle;
}
}

/// <summary>
///
Gets or sets a value indicating whether the time step must be fixed or not.
/// </summary>
/// <remarks>
///
If set to true, the game will target the desired constant framerate set in your main class ('Game1', by default).
/// </remarks>
public bool
IsFixedTimeStep
{
get { return this.Game.IsFixedTimeStep; }
set
{
if(this.Game != null)
this.Game.IsFixedTimeStep = value;
}
}

/// <summary>
///
Gets or sets a value indicating whether the framerate will display decimals on screen or not.
/// </summary>
public bool
ShowDecimals
{
get { return this.showDecimals; }
set { this.showDecimals = value; }
}

/// <summary>
///
Gets or sets a value indicating whether the decimal part of the framerate value must be display as fixed format (or as double format, otherwise).
/// </summary>
/// <remarks>
///
The 'ShowDecimals' property must be set to true in order to set the proper format.
/// </remarks>
public bool
FixedFormatDisplay
{
get { return this.displayFormat == "F"; }
set { this.displayFormat = value == true ? "F" : "R"; }
}

#endregion

#region Constructors

/// <summary>
/// Parameterless constructor for this class.

/// </summary>
public
Framerate()
{
InitializeComponent();
}

#endregion

#region Instance Methods

/// <summary>
/// Called after game initialization but before the first frame of the game.
/// </summary>
public override void Start()
{
this
.canDraw = false
;
this.currentFramerate = 0;

this.windowTitle = this.Game != null ? this.Game.Window.Title : String.Empty;
}

/// <summary>
///
Called when the gamecomponent needs to be updated.
/// </summary>
public override void
Update()
{
if (this.enabled)
{
// The time since Update() method was last called.
float elapsed = (float)this
.Game.ElapsedTime.TotalMilliseconds;

// Ads the elapsed time to the cumulative delta time.
this
.deltaFPSTime += elapsed;

// If delta time is greater than a second: (a) the framerate is calculated, (b) it is marked to be drawn, and (c) the delta time is adjusted, accordingly.
if (this.deltaFPSTime > 1000)
{
this.currentFramerate = 1000 / elapsed;
this.deltaFPSTime -= 1000;
this.canDraw = true;
}
}
}

/// <summary>

///
Called when the gamecomponent needs to be drawn.
/// </summary>
/// <remarks>
///
Currently, the framerate is shown in the window's title of the game.
/// </remarks>
public override void
Draw()
{
// If the framerate can be drawn, it is shown in the window's title of
the game.

if (this.visible && this.canDraw)
{
string currentFramerateString = this.showDecimals ? this.currentFramerate.ToString(this.displayFormat) : ((int)this.currentFramerate).ToString("D");

this.Game.Window.Title = "FPS: " + currentFramerateString;

this.canDraw = false;
}
}

#endregion
}
}

[We will discuss and focus on XNA GSE later.]