Monday 5 December 2011

CodeSample: Frame Counter


So you're in the process of making a game and you have got something drawn on the screen and some basic logic. But how well does it perform? One way of determining how well written it is (or basic as the case may be) is determining the frames per second. We've all heard this touted by various game developers;

"It runs at 30FPS"
"It runs at 60FPS"
"It runs at 60FPS for most of the time"

But how do we determine this? Well look no further. Just add my FrameCounter class to your game and you could find out. The component is quite simple and consists of mainly just a SpriteText variable with a couple of helper variables. So lets take a look.

First off we have the variables that will be used
/// 
/// The current frames during this second
/// 
private int _currentFrameCount;

/// 
/// The total time that has passed. This is used to determine 
/// when a second has passed.
/// 
private TimeSpan _timePassed;

/// 
/// Text representation of the current frame rate
/// 
private SpriteText _frameCountText;

/// 
/// The spritebatch to draw the frame rate to
/// 
private SpriteBatch _spriteBatch;

currentFrameCount allows us to keep track of the number of draw calls that have been made for the current second.

timePassed allows us to keep track of when a second is up

frameCountText allows us to draw the result on the screen.

and spriteBatch is to determine what we draw the text to.

Next we have the constuctor. Because the FPS is usually the first component I add and want on my screen, I've limited the display to the four corners of the screen. This positioning is perfomed in the constructor as displayed in the code snippet below.

/// <summary>
/// Constructor
/// </summary>
/// <param name="viewPort">The viewport that will determine the position of the frame counter</param>
/// <param name="spriteBatch">The spritebatch the frame count will be drawn to</param>
/// <param name="spriteFont">The font to draw the frame count in</param>
/// <param name="alignment">The area of the screen the frame count should be fixed to</param>
public FrameCounter(Viewport viewPort,
     SpriteBatch spriteBatch,
     SpriteFont spriteFont,
     Alignment alignment)
{
 if (alignment != Alignment.TopLeft &&
  alignment != Alignment.TopRight &&
  alignment != Alignment.BottomLeft &&
  alignment != Alignment.BottomRight)
 {
  throw new ArgumentException(String.Format("Alignment '{0}' is not supported",
              alignment));
 }

 _spriteBatch = spriteBatch;

 _currentFrameCount = 0;
 _timePassed = new TimeSpan();
 _frameCountText = new SpriteText(spriteFont,
          String.Format("FPS: 0"));
 _frameCountText.SetOrigin(alignment);
 switch (alignment)
 {
  case Alignment.TopLeft:
   _frameCountText.Position = new Vector2(viewPort.TitleSafeArea.Left,
               viewPort.TitleSafeArea.Top);
   break;
  case Alignment.TopRight:
   _frameCountText.Position = new Vector2(viewPort.TitleSafeArea.Right,
               viewPort.TitleSafeArea.Top);
   break;
  case Alignment.BottomLeft:
   _frameCountText.Position = new Vector2(viewPort.TitleSafeArea.Left,
               viewPort.TitleSafeArea.Bottom);
   break;
  case Alignment.BottomRight:
   _frameCountText.Position = new Vector2(viewPort.TitleSafeArea.Right,
               viewPort.TitleSafeArea.Bottom);
   break;
 }
}

Of course, you could alter this so that the position and origin properties of frameCountText are exposed in properties of their own on the FrameCounter object.

The last bit of the object is the Draw method. This method is to be called in the draw method of your game to get an accurate reading. As seen in the sample below, the method increments the currentFrameCount variable every time the method is called as well as adding the time passed through the gameTime parameter to the time stored in the timePassed variable. If the time stored in the timePassed is greater than a second, a second is deducted from the variable, the frameCountText 's text property is updated to display the value stored in the currentFrameCount variable and the variable is then reset to zero to continue counting the frames for the next second.

/// <summary>
/// Draw the frame count
/// </summary>
/// <remarks>
/// SpriteBatch.Begin must be called before this is
/// </remarks>
/// <param name="gameTime">A snapshot of time passed</param>
public void Draw(GameTime gameTime)
{
 _currentFrameCount++;

 _timePassed = _timePassed.Add(new TimeSpan(gameTime.ElapsedGameTime.Ticks));
 if (_timePassed.Seconds >= 1)
 {
  _timePassed = _timePassed.Subtract(new TimeSpan(0, 0, 1));
  _frameCountText.Text = String.Format("FPS: {0}",
            _currentFrameCount);
  _currentFrameCount = 0;
 }

 _frameCountText.Draw(_spriteBatch);
}

And that concludes my FrameCounter object. It's simple, yet essential for any game developer. Again, the full code sample is available for download.

No comments:

Post a Comment

Got an opinion? Who hasn't? Post it here...