Game Creation with XNA/2D Development/Heads-Up-Display

Heads-Up-Display

edit

A Heads-Up-Display (short HUD) is any transparent display that presents information without requiring users to look away from their usual viewpoints. The origin of the name stems from the modern aircraft pilots being able to view information with heads "up" and looking forward, instead of angled down looking at lower instruments.

Although they were initially developed for military aviation, HUDs are now used in commercial aircraft, automobiles, and even in todays game design. There the HUD relays information to the player as part of a game's user interface.

This article will feature examples for HUD elements and XNA templates for some of these basic components. Since good sprites are really important for creating a great looking HUD, designing these with professional image processing applications, such as Gimp or Photoshop is vital. Developing the skills will not be part of this article.

Introduction

edit

Application

edit

There are many different types of information that can be displayed using a HUD. Below is an outline of the most important stats displayed on video game HUDs

Health & lives
edit

Health is of extreme importance. Hence this is one of the most important HUD Stats on display. This contains information about the player's character or about NPC's, such as allies and enemies. RTS games (e.g. Starcraft) usually display the health level of all units that are visible on screen. In many action oriented games (first- or third-person shooters) the screen flashes briefly, when the player is attacked, and shows arrows indicating the direction the threat came from.

Weapons & items
edit

Most action games (first- and third-person shooters in particular) show information about the weapons currently used, ammunition left, other weapons, objects or items that are available.

edit

Menus for different game related aspects (e.g. start game, exit game or change settings).

Time
edit
 
HUD of the RTS game Warzone 2100.

This contains timer counting up or down to display information about certain events (e.g. end of round), records such as lap times or the length of time a player can last in survival based game. HUDS can be used to display in-game time (time, day, year within the game) or even show real time.

Context-sensitive Information
edit

This contains information that are only shown when necessary or important (e.g. tutorial messages, one/off abilities, subtitles or action events).

Game progression
edit

This contains information about the player's current game progress (e.g. stats on a gamer's progress within one particular task or quest, accumulated experience points or a gamer's current level). It also includes information about the player's current task.

Mini-maps, Compass, Quest-Arrow
edit

Games are all about reaching objectives, so HUDs must clearly state them, either in the form of a compass or quest arrow. A small map of the area that can act like a radar, showing the terrain, allies and/or enemies, locations like safe houses and shops or streets.

Speedometer
edit

Used in most games that feature drivable vehicles. Usually shown only when driving one of these.

Cursor & Crosshair
edit

The crosshair indicates the direction the player is pointing or aiming to.

Examples

edit
  1. Beautiful video game HUD designs
  2. Great HUDs in gaming
  3. Games without using HUDs

Less is more

edit

In order to increase realism information normally displayed using a HUD can be instead disguised as part of the scenery or part of the vehicle the player is using. For example, when the player is driving a car that can sustain a certain number of hits, a smoke trail or fire might appear from the car to indicate that the car is seriously damaged and will break down soon. Wounds and bloodstains may sometimes appear on injured characters who may also limp or breathe heavily to indicate that they are injured.

In some cases, no HUD is displayed at all. Leaving the player to interpret the auditory and visual cues in the game world creates a more intense athmosphere.

Text in HUD

edit

Every font installed on your computer can be used to display text in your HUD. Therefore the font has to be added as an "Existing file" to the project in Visual Studio. Afterwards a .spritefont (XML) file can be found in the content folder of your project. There all parameters, such as style, size or kerning, can be easily configured.

Loading fonts

edit
SpriteFont spriteFont = contentManager.Load<SpriteFont>("Path//Fontname");

Displaying fonts

edit
spriteBatch.DrawString(spriteFont, textLabel + ": " + textValue, position, textColor);

(Semi-)Transparency

edit
Color myTransparentColor = new Color(0, 0, 0, 127);

Background

edit
Rectangle rectangle = new Rectangle();
rectangle.Width = spriteFont.MeasureString(text).X + 10;
rectangle.Height = spriteFont.MeasureString(text).Y + 10;

Texture2D texture = new Texture2D(graphicsDevice, 1, 1);
texture.SetData(new Color[] {color});

spriteBatch.Draw(texture, rectangle, color);

Images in HUD

edit

Since there is no concept of drawing on canvas elements, images or sprites are an important element for creating HUDs. XNA supports many different image formats, such as .jpeg or .png (including transparency).

Loading Images

edit
contentManager.Load<Texture2D>("Path//Filename")

or you could try this one :

contentManager.Load<Texture2D>(@"Path/Filename")

With this approach we use the default "content" folder and the "doubled" ("//") slash is not necessary.

Displaying images

edit
spriteBatch.Draw(image, position, null, color, 0 , new Vector2(backgroundImage.Width/2, backgroundImage.Height/2), scale, SpriteEffects.None, 0);

Components

edit

The following components are templates that are ready to use. They can be easily customized to fit the individual requirements.

Text

edit
 
Text HUD component in XNA game.
Information
edit

This component displays a text field. It can be used to display a big variety of information, such as time, scores or objectives. In order to increase readability a semi transparent background is displayed behind the text.

Class variables
edit
private SpriteBatch spriteBatch;
private SpriteFont spriteFont;
private GraphicsDevice graphicsDevice;

private Vector3 position;

private String textLabel;
private String textValue;
private Color textColor;

private bool enabled;
Constructor
edit
/// <summary>
/// Creates a new TextComponent for the HUD.
/// </summary>
/// <param name="textLabel">Label text that is displayed before ":".</param>
/// <param name="position">Component position on the screen.</param>
/// <param name="spriteBatch">SpriteBatch that is required to draw the sprite.</param>
/// <param name="spriteFont">Font that will be used to display the text.</param>
/// <param name="graphicsDevice">Graphicsdevice that is required to create the semi transparent background texture.</param>
public TextComponent(String textLabel, Vector2 position, SpriteBatch spriteBatch, SpriteFont spriteFont, GraphicsDevice graphicsDevice)
   {
   this.textLabel = textLabel.ToUpper();
   this.position = position;
            
   this.spriteBatch = spriteBatch;
   this.spriteFont = spriteFont;
   this.graphicsDevice = graphicsDevice;
   }
Enable
edit
/// <summary>
/// Sets whether the component should be drawn.
/// </summary>
/// <param name="enabled">enable the component</param>
public void Enable(bool enabled)
   {
   this.enabled = enabled;
   }
Update
edit
/// <summary>
/// Updates the text that is displayed after ":".
/// </summary>
/// <param name="textValue">Text to be displayed.</param>
/// <param name="textColor">Text color.</param>
public void Update(String textValue, Color textColor)
   {
   this.textValue = textValue.ToUpper();
   this.textColor = textColor;
   }
Draw
edit
/// <summary>
/// Draws the TextComponent with the values set before.
/// </summary>
public void Draw()
   {
   if (enabled)
      {
      Color myTransparentColor = new Color(0, 0, 0, 127);
      
      Vector2 stringDimensions = spriteFont.MeasureString(textLabel + ": " + textValue);
      float width = stringDimensions.X;
      float height = stringDimensions.Y;

      Rectangle backgroundRectangle = new Rectangle();
      backgroundRectangle.Width = (int)width + 10;
      backgroundRectangle.Height = (int)height + 10;
      backgroundRectangle.X = (int)position.X - 5;
      backgroundRectangle.Y = (int)position.Y - 5;

      Texture2D dummyTexture = new Texture2D(graphicsDevice, 1, 1);
      dummyTexture.SetData(new Color[] { myTransparentColor });

      spriteBatch.Draw(dummyTexture, backgroundRectangle, myTransparentColor);
      spriteBatch.DrawString(spriteFont, textLabel + ": " + textValue, position, textColor);
      }
   }

Meter

edit
 
Meter HUD component in XNA game.
Information
edit

This component displays a round instrument. It can be used to display a big variety of information, such as speed, rounds, fuel, height/depth, angle or temperature. The background image is displayed at the passed position. The needle image is rotated accordingly to the ratio between maximum and current value. The rotation angle is interpolated to create a smooth, life like impression.

Class variables
edit
private SpriteBatch spriteBatch;

private const float MAX_METER_ANGLE = 230;
private bool enabled = false;

private float scale;
private float lastAngle;

private Vector2 meterPosition;
private Vector2 meterOrigin;

private Texture2D backgroundImage;
private Texture2D needleImage;

public float currentAngle = 0;
Constructor
edit
/// <summary>
/// Creates a new TextComponent for the HUD.
/// </summary>
/// <param name="position">Component position on the screen.</param>
/// <param name="backgroundImage">Image for the background of the meter.</param>
/// <param name="needleImage">Image for the neede of the meter.</param>
/// <param name="spriteBatch">SpriteBatch that is required to draw the sprite.</param>
/// <param name="scale">Factor to scale the graphics.</param>
public MeterComponent(Vector2 position, Texture2D backgroundImage, Texture2D needleImage, SpriteBatch spriteBatch, float scale)
   {
   this.spriteBatch = spriteBatch;
   
   this.backgroundImage = backgroundImage;
   this.needleImage = needleImage;
   this.scale = scale;
   
   this.lastAngle = 0;

   meterPosition = new Vector2(position.X + backgroundImage.Width / 2, position.Y + backgroundImage.Height / 2);
   meterOrigin = new Vector2(52, 18);
   }
Enable
edit
/// <summary>
/// Sets whether the component should be drawn.
/// </summary>
/// <param name="enabled">enable the component</param>
public void Enable(bool enabled)
   {
   this.enabled = enabled;
   }
Update
edit
/// <summary>
/// Updates the current value of that should be displayed.
/// </summary>
/// <param name="currentValue">Value that to be displayed.</param>
/// <param name="maximumValue">Maximum value that can be displayed by the meter.</param>
public void Update(float currentValue, float maximumValue)
   {
   currentAngle = MathHelper.SmoothStep(lastAngle, (currentValue / maximumValue) * MAX_METER_ANGLE, 0.2f);
   lastAngle = currentAngle;
   }
Draw
edit
/// <summary>
/// Draws the MeterComponent with the values set before.
/// </summary>
public void Draw()
   {
   if (enabled)
      {
      spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Immediate, SaveStateMode.SaveState);
      spriteBatch.Draw(backgroundImage, meterPosition, null, Color.White, 0, new Vector2(backgroundImage.Width / 2, backgroundImage.Height / 2), scale, SpriteEffects.None, 0); //Draw(backgroundImage, position, Color.White);
      spriteBatch.Draw(needleImage, meterPosition, null, Color.White, MathHelper.ToRadians(currentAngle), meterOrigin, scale, SpriteEffects.None, 0);
      spriteBatch.End();
      }
   }

Radar

edit
 
Radar HUD component in XNA game.
Information
edit

This component displays a radar map. It can be used to display a big variety of information, such as objective or enemies. The background image is displayed at the passed position. Dots representing objects in the map are displayed accordingly to an array of positions.

Class variables
edit
private SpriteBatch spriteBatch;
GraphicsDevice graphicsDevice;

private bool enabled = false;

private float scale;
private int dimension;

private Vector2 position;

private Texture2D backgroundImage;

public float currentAngle = 0;

private Vector3[] objectPositions;
private Vector3 myPosition;
private int highlight;
Constructor
edit
/// <summary>
/// Creates a new RadarComponent for the HUD.
/// </summary>
/// <param name="position">Component position on the screen.</param>
/// <param name="backgroundImage">Image for the background of the radar.</param>
/// <param name="spriteBatch">SpriteBatch that is required to draw the sprite.</param>
/// <param name="scale">Factor to scale the graphics.</param>
/// <param name="dimension">Dimension of the world.</param>
/// <param name="graphicsDevice">Graphicsdevice that is required to create the textures for the objects.</param>
public RadarComponent(Vector2 position, Texture2D backgroundImage, SpriteBatch spriteBatch, float scale, int dimension, GraphicsDevice graphicsDevice)
   {
   this.position = position;

   this.backgroundImage = backgroundImage;

   this.spriteBatch = spriteBatch;
   this.graphicsDevice = graphicsDevice;

   this.scale = scale;
   this.dimension = dimension;
   }
Enable
edit
/// <summary>
/// Sets whether the component should be drawn.
/// </summary>
/// <param name="enabled">enable the component</param>
public void Enable(bool enabled)
   {
   this.enabled = enabled;
   }
Update
edit
/// <summary>
/// Updates the positions of the objects to be drawn and the angle for the rotation of the radar.
/// </summary>
/// <param name="objectPositions">Position of all objects to be drawn.</param>
/// <param name="highlight">Index of the object to be highlighted. Object with a smaller or a 
/// greater index will be displayed in a smaller size and a different color.</param>
/// <param name="currentAngle">Angle for the rotation of the radar.</param>
/// <param name="myPosition">Position of the player.</param>
public void update(Vector3[] objectPositions, int highlight, float currentAngle, Vector3 myPosition)
   {
   this.objectPositions = objectPositions;
   this.highlight = highlight;
   this.currentAngle = currentAngle;
   this.myPosition = myPosition;
   }
Draw
edit
/// <summary>
/// Draws the RadarComponent with the values set before.
/// </summary>
public void Draw()
   {
   if (enabled)
      {
      spriteBatch.Draw(backgroundImage, position, null, Color.White,0 , new Vector2(backgroundImage.Width / 2, backgroundImage.Height / 2), scale, SpriteEffects.None, 0);
                
       for(int i = 0; i< objectPositions.Length; i++)
          {
          Color myTransparentColor = new Color(255, 0, 0);
          if (highlight == i)
             {
             myTransparentColor = new Color(255, 255, 0);
             }
          else if(highlight > i)
             {
             myTransparentColor = new Color(0, 255, 0);
             }

          Vector3 temp = objectPositions[i];
          temp.X = temp.X / dimension * backgroundImage.Width / 2 * scale;
          temp.Z = temp.Z / dimension * backgroundImage.Height / 2 * scale;

          temp = Vector3.Transform(temp, Matrix.CreateRotationY(MathHelper.ToRadians(currentAngle)));

          Rectangle backgroundRectangle = new Rectangle();
          backgroundRectangle.Width = 2;
          backgroundRectangle.Height = 2;
          backgroundRectangle.X = (int) (position.X + temp.X);
          backgroundRectangle.Y = (int) (position.Y + temp.Z);

          Texture2D dummyTexture = new Texture2D(graphicsDevice, 1, 1);
          dummyTexture.SetData(new Color[] { myTransparentColor });

          spriteBatch.Draw(dummyTexture, backgroundRectangle, myTransparentColor);
          }

       myPosition.X = myPosition.X / dimension * backgroundImage.Width / 2 * scale;
       myPosition.Z = myPosition.Z / dimension * backgroundImage.Height / 2 * scale;

       myPosition = Vector3.Transform(myPosition, Matrix.CreateRotationY(MathHelper.ToRadians(currentAngle)));

       Rectangle backgroundRectangle2 = new Rectangle();
       backgroundRectangle2.Width = 5;
       backgroundRectangle2.Height = 5;
       backgroundRectangle2.X = (int)(position.X + myPosition.X);
       backgroundRectangle2.Y = (int)(position.Y + myPosition.Z);

       Texture2D dummyTexture2 = new Texture2D(graphicsDevice, 1, 1);
       dummyTexture2.SetData(new Color[] { Color.Pink });

       spriteBatch.Draw(dummyTexture2, backgroundRectangle2, Color.Pink);
       }
   }
 
Bar HUD component in XNA game.
Information
edit

This component displays a bar. I can be used to display any kind of information that is related to percentages (e.g. fuel, health or time left to reach an objective). The current percent value is represented by the length of the colore bar. Accordingly to the displayed value, the color changes from green over yellow to red.

Class variables
edit
 private SpriteBatch spriteBatch;
private GraphicsDevice graphicsDevice;

private Vector2 position;
private Vector2 dimension;

private float valueMax;
private float valueCurrent;

private bool enabled;
Constructor
edit
/// <summary>
/// Creates a new Bar Component for the HUD.
/// </summary>
/// <param name="position">Component position on the screen.</param>
/// <param name="dimension">Component dimensions.</param>
/// <param name="valueMax">Maximum value to be displayed.</param>
/// <param name="spriteBatch">SpriteBatch that is required to draw the sprite.</param>
/// <param name="graphicsDevice">Graphicsdevice that is required to create the semi transparent background texture.</param>
public BarComponent(Vector2 position, Vector2 dimension, float valueMax, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
   {
   this.position = position;
   this.dimension = dimension;
   this.valueMax = valueMax;
   this.spriteBatch = spriteBatch;
   this.graphicsDevice = graphicsDevice;
   this.enabled = true;
   }
Enable
edit
/// <summary>
/// Sets whether the component should be drawn.
/// </summary>
/// <param name="enabled">enable the component</param>
public void enable(bool enabled)
   {
   this.enabled = enabled;
   }
Update
edit
/// <summary>
/// Updates the text that is displayed after ":".
/// </summary>
/// <param name="valueCurrent">Text to be displayed.</param>
public void update(float valueCurrent)
   {
   this.valueCurrent = valueCurrent;
   }
Draw
edit
/// <summary>
/// Draws the BarComponent with the values set before.
/// </summary>
public void Draw()
   {
   if (enabled)
      {
      float percent = valueCurrent / valueMax;

      Color backgroundColor = new Color(0, 0, 0, 128);
      Color barColor = new Color(0, 255, 0, 200);
      if (percent < 0.50)
         barColor = new Color(255, 255, 0, 200);
      if (percent < 0.20)
         barColor = new Color(255, 0, 0, 200);

      Rectangle backgroundRectangle = new Rectangle();
      backgroundRectangle.Width = (int)dimension.X;
      backgroundRectangle.Height = (int)dimension.Y;
      backgroundRectangle.X = (int)position.X;
      backgroundRectangle.Y = (int)position.Y;

      Texture2D dummyTexture = new Texture2D(graphicsDevice, 1, 1);
      dummyTexture.SetData(new Color[] { backgroundColor });

      spriteBatch.Draw(dummyTexture, backgroundRectangle, backgroundColor);

      backgroundRectangle.Width = (int)(dimension.X*0.9);
      backgroundRectangle.Height = (int)(dimension.Y*0.5);
      backgroundRectangle.X = (int)position.X + (int)(dimension.X * 0.05);
      backgroundRectangle.Y = (int)position.Y + (int)(dimension.Y*0.25);

      spriteBatch.Draw(dummyTexture, backgroundRectangle, backgroundColor);

      backgroundRectangle.Width = (int)(dimension.X * 0.9 * percent);
      backgroundRectangle.Height = (int)(dimension.Y * 0.5);
      backgroundRectangle.X = (int)position.X + (int)(dimension.X * 0.05);
      backgroundRectangle.Y = (int)position.Y + (int)(dimension.Y * 0.25);

      dummyTexture = new Texture2D(graphicsDevice, 1, 1);
      dummyTexture.SetData(new Color[] { barColor });

      spriteBatch.Draw(dummyTexture, backgroundRectangle, barColor);
      }
   }
edit

UI game design

edit
  1. Video game interface design
  2. Rethinking HUD in game design
  3. Thoughts on HUDs

HUD design in Photoshop

edit
  1. Ironman view interface
  2. High tech style HUD rings

Resources

edit
  1. Fonts for HUDs

References

edit
  1. Beginning XNA 3.0 Game Programming: From Novice to Professional; Alexandre Santos Lobão, Bruno Evangelista, José Antonio Leal de Farias, Riemer Grootjans, 2009
  2. Microsoft® XNA Game Studio 3.0 UNLEASHED; Chad Carter; 2009
  3. Microsoft® XNA Game Studio Creator's Guide: An Introduction to XNA Game Programming; Stephen Cawood, Pat McGee, 2007


Authors

edit

Christian Höpfner