Showing posts with label codesample. Show all posts
Showing posts with label codesample. Show all posts

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.

Tuesday, 1 November 2011

Two Months, One Code Sample and Half an Update


Wow! Just Wow. 2 months since my last post is very bad on my part. Especially as I've been making progress (abeit slowly) on at least one of my projects.
From my last post I was hoping to do an initial post on each of my projects, and then have weekly or bi-monthly updates on the progress of those projects. However, they're all still quite rough looking and not much to say. However, we'll see. I might change my mind. But even though the posts have been thin, I've been making progress on at least one of my projects and have ended up creating a lot of, what I think are, useful components. Because of this, I thought I would start sharing them with my fellow readers. So if you're not a developer you may want to avoid any post from now on that has the tag "codesample". I must also warn that all code samples will be for XNA (determined by the "XNA" tag) for the foreseeable future, but this of course could change. But anyway...on with the code.

SpriteText - A text container with a twist

In order to describe and show some of my more useful components, I need to really start with some of my more basic components. This one is basically a container for all information that relates to drawing text to the screen. This
container contains public methods for each of the required parameters in the SpriteBatch.DrawString method. This is as follows:

/// <summary>
/// The font to draw the text in
/// </summary>
public SpriteFont Font { get; set; }

/// <summary>
/// The text to draw
/// </summary>
public string Text 
{
 get
 {
  return _text;
 }
 set
 {
  _text = value;
  this.Size = new Vector2(Font.MeasureString(value).X * this.Scale.X,
  Font.MeasureString(value).Y * this.Scale.Y);
  this.SetOrigin(_currentOrigin);
 }
}

/// <summary>
/// The position to draw the text at
/// </summary>
public Vector2 Position { get; set; }

/// </summary>
/// The size of the text
/// </summary>
public Vector2 Size
{
 get 
 {
  return _size;
 }
 private set
 {
  _size = value;
  _size = Vector2.Transform(_size, Matrix.CreateRotationZ(_rotation));
 }
}

/// <summary>
/// The colour to draw the text in
/// </summary>
/// <remarks>
/// Default is white
/// </remarks>
public Color Colour { get; set; }

/// <summary>
/// The rotation to draw the text
/// </summary>
public float Rotation 
{
 get
 {
  return _rotation;
 }
 set
 {
  _rotation = value;
  this.Size = new Vector2(Font.MeasureString(_text).X * _scale.X,
  Font.MeasureString(_text).Y * _scale.Y);
 }
}

/// <summary>
/// The origin of the text
/// </summary>
/// <remarks>
/// Default is the centre of the text
/// </remarks>
public Vector2 Origin { get; protected set; }

/// <summary>
/// The scale at which to draw the text in
/// </summary>
public Vector2 Scale
{
 get
 {
  return _scale;
 }
 set
 {
  _scale = value;
  _size = new Vector2(Font.MeasureString(_text).X * _scale.X,
  Font.MeasureString(_text).Y * _scale.Y);
 }
}

/// <summary>
/// The effect to apply to the text
/// </summary>
/// <remarks>
/// Default is none
/// </remarks>
public SpriteEffects Effect { get; set; }

/// <summary>
/// The depth at which the text is drawn
/// </summary>
/// <remarks>
/// By default this is 0
/// </remarks>
public float LayerDepth { get; set; }

I feel this is all very self explainatory. Most of the properties are automatic. The ones that aren't have corresponding protected variables. Scale and Rotation both change the Size property value when changed as these effect the overall size of the text object. Text sets a text property but then calls a method called SetOrigin passing in a private variable call _currentOrigin. "What's this doing?" I hear you ask. Well let me explain.

The twist that SpriteText does is it auto adjusts the origin value that is used when drawing the text so that it will always appear in the same location when the text is changed. Take the following example; You want to use the sprite text object to display a score on the right hand side of the screen. Now you can do this two ways; you can have origin set to Vector2.Zero and calculate the position taking into account the length of the text, or you can set the position to the far right and adjust the origin to the length of the text.


This is fine if your text doesn't change, but as soon as the length of the text changes, you need to change the value of either the origin or the position. As I'm a programmer, I'm lazy and want this done automagically. So incomes UpdateOrigin and _currentOrigin. _currentOriginis set in the constructor of SpriteText (along with other defaults for the other properties) and is a custom enum called Alignment (the code of which can be found below)

/// <summary>
/// Constructor
/// </summary>
/// <param name="spriteBatch">The sprite batch to draw the text to</param>
/// <param name="font">The font to draw the text in</param>
/// <param name="text">The text to draw</param>
public SpriteText(SpriteBatch spriteBatch, SpriteFont font, string text)
{
 this.Font = font;
 _currentOrigin = Alignment.Centre;
 this._spriteBatch = spriteBatch;
 this.Colour = Color.White;
 this.Scale = Vector2.One;
 this.Effect = SpriteEffects.None;
 this.Text = text;
 this.LayerDepth = 0;
}

/// <summary>
/// Represents the different alignments an object could be placed in
/// </summary>
public enum Alignment
{
 None,
 TopLeft,
 Top,
 TopRight,
 Right,
 BottomRight,
 Bottom,
 BottomLeft,
 Left,
 Centre
}

SetOrigin (which is a public method that can be called to change the origin "on the fly") takes an Alignment parameter and updates the origin/position values for the SpriteText object so it will always appear in the correct position on the screen. The code of which is below

/// <summary>
/// Set the origin of text
/// </summary>
/// <param name="origin">The origin alignment</param>
public void SetOrigin(Alignment origin)
{
 _currentOrigin = origin;

 switch (origin)
 {
  case Alignment.Left:
   this.Origin = new Vector2(0.0f,
   this.Font.MeasureString(this.Text).Y / 2);
   break;
  case Alignment.TopLeft:
   this.Origin = new Vector2(0.0f,
   0.0f);
   break;
  case Alignment.Top:
   this.Origin = new Vector2(this.Font.MeasureString(this.Text).X / 2,
   0.0f);
   break;
  case Alignment.TopRight:
   this.Origin = new Vector2(this.Font.MeasureString(this.Text).X,
   0.0f);
   break;
  case Alignment.Right:
   this.Origin = new Vector2(this.Font.MeasureString(this.Text).X,
   this.Font.MeasureString(this.Text).Y / 2);
   break;
  case Alignment.BottomRight:
   this.Origin = new Vector2(this.Font.MeasureString(this.Text).X,
   this.Font.MeasureString(this.Text).Y);
   break;
  case Alignment.Bottom:
   this.Origin = new Vector2(this.Font.MeasureString(this.Text).X / 2,
   this.Font.MeasureString(this.Text).Y);
   break;
  case Alignment.BottomLeft:
   this.Origin = new Vector2(0.0f,
   this.Font.MeasureString(this.Text).Y);
   break;
  case Alignment.Centre:
   this.Origin = new Vector2(this.Font.MeasureString(this.Text).X / 2,
   this.Font.MeasureString(this.Text).Y / 2);
   break;
  default:
  throw new ArgumentException("Alignment not supported");
 }
}

Then there is the standard draw method which just calls the SpriteBatch.DrawString method passing in the corresponding methods. As you can see it doesn't do anything special but I find it useful when wanting to draw text on the screen in XNA. The full code sample is available to download.