Robot Foot Games Helping you improve your games as we improve our own. :)

23May/110

Text Helper Class

Here's a little text class that will help you keep things organized and if you have a bunch of SpriteBatch.DrawString(...) calls. It also has text alignment as you can see in the picture below and that comes in pretty handy. If you want to align your text to the very top right then specify the very top right position. Ex: new Vector2(viewport.Width, 0) or BottomCenter would look like this: new Vector2(viewport.Width / 2, viewport.Height).

textClass

UPDATE(July 5, 2010)

    • Project download. I added a zip containing the project so you can see how things are laid out if you’re having troubles. Download link follows.

  • Text wrapper. Example: string myText = TextHelper.WrapText(font, "add lots of text if you want here", width);. Width is the amount of space until a new line is started. A value of 400 would mean that each line can be up to 400 pixels wide before a new line is started.
  • Auto updating. Whenever you change the text the fontSize and fontOrigin are updated through the UpdateText(...) function. Now if you have something aligned on the right side of the screen and you change its text it will update and display correctly (it didn’t align correctly beforehand). This happens automatically whenever you change the FontText variable. Example:TextList[0].FontText = “Hi”;
  • Naming convention. I also change how the TextHelper objects are stored so instead of using TextList[3].FontText... to access and change the text you assign names to each TextHelper object so they're easy to remember and access. At the top of your class you now need to create all of your TextHelper objects. Example: TextHelper text_score; or TextHelper text_lives; etc. Then in your LoadContent(…) method where you previously used TextList.Add(…) you can replace those withTextList.Add(text_score); or TextList.Add(text_lives); etc. This is a tiny bit more work but it pays off in the end. Instead of using TextList[2].FontText = “new text”; you can use text_lives.FontText = “99”;. It’s a lot easier to deal with names than numbers so this is more of a usability update then anything.
  • Fading text. I’m using Nick Gravelyn’s timer/interpolator code because it’s awesome and works perfectly for this. You’ll need to download it here and add it to your project. Quick run-through of his code - timers are used to create timers - interpolators are used to gradually change things. Since fading gradually changes the alpha value of our text using an interpolator makes perfect sense. You should read through his blog post about it before you move on to get a more complete explanation, “Using interpolators and timers”. An Update(…) method was added to the TextHelper class and just as you draw all the objects using foreach(TextHelper t in TextList) { t.Draw(spriteBatch); } we now need to do that in our Update(…) method. Add this: foreach(TextHelper t in TextList) { t.Update(gameTime); }
  • Pulsing text. This just fades the text in/out and allows you to say how quick it should do so. Example: text_lives.Pulsate(3);will fade out for 3 seconds, fade in for 3 seconds, repeat.

That’s it for this update. I just gave a quick overview of the functions above so download the project if you're having trouble understanding it all.

 

Here's the TextHelper class in its entirety:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Utility;
namespace TextHelper
{
    enum TextAlignment
    {
        TopLeft = 0,
        MiddleLeft = 1,
        BottomLeft = 2,
        TopCenter = 3,
        MiddleCenter = 4,
        BottomCenter = 5,
        TopRight = 6,
        MiddleRight = 7,
        BottomRight = 8,
    }
    class TextHelper
    {
        //Font to use
        private SpriteFont font;
        //Change the size of our font on-screen
        private float fontScale;
        //Size of our font. We also update this when fontText changes.
        private Vector2 fontSize;
        //Color of the font
        public Color fontColor;
        //Position of the text on screen
        private Vector2 fontPosition;
        //Text to draw to the screen
        private string fontText;
        public string FontText
        {
            get { return fontText; }
            set
            {
                fontText = value;
                //Whenever we update the text we also need to update
                //fontSize and fontOrigin and UpdateText() does just that
                UpdateText();
            }
        }
        //Used for aligning our text in different ways
        private Vector2 fontOrigin;
        TextAlignment fontAlignment = TextAlignment.TopLeft;
        //Timers and interpolators. Used for fading text.
        private TimerCollection timers = new TimerCollection();
        private InterpolatorCollection interpolators = new InterpolatorCollection();
        //Used to tell if we're fading in/out
        bool isFadingIn;
        /// <summary>
        /// Constructor WITHOUT alignment assigned default to TextAlignment.TopLeft
        /// </summary>
        /// <param name="spriteFont">font to use</param>
        /// <param name="text">text to display</param>
        /// <param name="position">position of text on-screen</param>
        /// <param name="scale">scale of the text</param>
        /// <param name="color">color of the text</param>
        public TextHelper(SpriteFont spriteFont, string text,
                          Vector2 position, float scale, Color color)
        : this(spriteFont, text, position, TextAlignment.TopLeft, scale, color)
        {
        }
        /// <summary>
        /// Constructor used to specify textAlignment
        /// </summary>
        /// <param name="spriteFont">font to use</param>
        /// <param name="text">text to display</param>
        /// <param name="position">position of text on-screen</param>
        /// <param name="alignment">alignment of the text</param>
        /// <param name="scale">scale of the text</param>
        /// <param name="color">color of the text</param>
        public TextHelper(SpriteFont spriteFont, string text, Vector2 position,
                          TextAlignment alignment, float scale, Color color)
        {
            font = spriteFont;
            fontText = text;
            fontPosition = position;
            fontAlignment = alignment;
            fontScale = scale;
            fontColor = color;
            fontOrigin = Vector2.Zero;
            fontSize = font.MeasureString(text);
            //Horizontal alignment
            if (fontAlignment == TextAlignment.TopCenter ||
                fontAlignment == TextAlignment.MiddleCenter ||
                fontAlignment == TextAlignment.BottomCenter)
            {
                fontOrigin.X = fontSize.X / 2;
            }
            else if (fontAlignment == TextAlignment.TopRight ||
                     fontAlignment == TextAlignment.MiddleRight ||
                     fontAlignment == TextAlignment.BottomRight)
            {
                fontOrigin.X = fontSize.X;
            }
            //Vertical alignment
            if (fontAlignment == TextAlignment.MiddleLeft ||
                fontAlignment == TextAlignment.MiddleCenter ||
                fontAlignment == TextAlignment.MiddleRight)
            {
                fontOrigin.Y = fontSize.Y / 2;
            }
            else if (fontAlignment == TextAlignment.BottomLeft ||
                     fontAlignment == TextAlignment.BottomCenter ||
                     fontAlignment == TextAlignment.BottomRight)
            {
                fontOrigin.Y = fontSize.Y;
            }
        }
        public void Update(GameTime gameTime)
        {
            timers.Update(gameTime);
            interpolators.Update(gameTime);
        }
        private void UpdateText()
        {
            fontSize = font.MeasureString(fontText);
            //Horizontal alignment
            if (fontAlignment == TextAlignment.TopCenter ||
                fontAlignment == TextAlignment.MiddleCenter ||
                fontAlignment == TextAlignment.BottomCenter)
            {
                fontOrigin.X = fontSize.X / 2;
            }
            else if (fontAlignment == TextAlignment.TopRight ||
                     fontAlignment == TextAlignment.MiddleRight ||
                     fontAlignment == TextAlignment.BottomRight)
            {
                fontOrigin.X = fontSize.X;
            }
            //Vertical alignment
            if (fontAlignment == TextAlignment.MiddleLeft ||
                fontAlignment == TextAlignment.MiddleCenter ||
                fontAlignment == TextAlignment.MiddleRight)
            {
                fontOrigin.Y = fontSize.Y / 2;
            }
            else if (fontAlignment == TextAlignment.BottomLeft ||
                     fontAlignment == TextAlignment.BottomCenter ||
                     fontAlignment == TextAlignment.BottomRight)
            {
                fontOrigin.Y = fontSize.Y;
            }
        }
        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.DrawString(font, fontText, fontPosition, fontColor,
                0f, fontOrigin, fontScale, SpriteEffects.None, 0);
        }
        public static string WrapText(SpriteFont spriteFont, string text, float maxLineWidth)
        {
            string[] words = text.Split(' ');
            StringBuilder sb = new StringBuilder();
            float lineWidth = 0f;
            float spaceWidth = spriteFont.MeasureString(" ").X;
            foreach (string word in words)
            {
                Vector2 size = spriteFont.MeasureString(word);
                if (lineWidth + size.X < maxLineWidth)
                {
                    sb.Append(word + " ");
                    lineWidth += size.X + spaceWidth;
                }
                else
                {
                    sb.Append("\n" + word + " ");
                    lineWidth = size.X + spaceWidth;
                }
            }
            return sb.ToString();
        }
        public void FadeIn(float howLongToFadeIn)
        {
            //Changing howLongToFadeIn will increase/decrease the fade in duration
            interpolators.Create(0, 1, howLongToFadeIn, FadeInInterpolator, null);
            isFadingIn = true;
        }
        private void FadeInInterpolator(Interpolator i)
        {
            //Color.Lerp does a linear interpolation
            //between the colors specified.
            // Argument #1 = transparent (see-through)
            // Argument #2 = opaque (fully visible)
            fontColor = Color.Lerp(
               new Color(fontColor, 0),
               new Color(fontColor, 255),
               i.Value);
        }
        public void FadeOut(float howLongToFadeOut)
        {
            //Changing howLongToFadeOut will increase/decrease the fade in duration
            interpolators.Create(0, 1, howLongToFadeOut, FadeOutInterpolator, null);
            isFadingIn = false;
        }
        private void FadeOutInterpolator(Interpolator i)
        {
            //Color.Lerp does a linear interpolation
            //between the colors specified.
            // Argument #1 = opaque (fully visible)
            // Argument #2 = transparent (see-through)
            fontColor = Color.Lerp(
               new Color(fontColor, 255),
               new Color(fontColor, 0),
               i.Value);
        }
        public void Pulsate(float howLongBetweenEachBurst)
        {
            //We begin at full alpha so the only
            //way to go is to start fading out
            isFadingIn = false;
            //Since text starts at fully visible then we'll fade out right away.
            interpolators.Create(0, 1, howLongBetweenEachBurst, FadeOutInterpolator, null);
            //create a timer that will active once the first fade out occurs
            timers.Create(howLongBetweenEachBurst, true, timer =>
            {
                if (isFadingIn)
                    interpolators.Create(0, 1, howLongBetweenEachBurst, FadeOutInterpolator,
                        interpolator => { isFadingIn = false; });
                else
                    interpolators.Create(0, 1, howLongBetweenEachBurst, FadeInInterpolator,
                        interpolator => { isFadingIn = true; });
            });
        }
    }
}

 

Create a new class called 'TextHelper.cs', replace its contents with the class above and change the namespace to match the namespace of your project.


 

Now for the implementation! To get it to work in your game you'll need a SpriteFont to draw the strings, a TextHelper variable for the actual string and (optional but recommended if you have lots of strings) a List of your TextHelper objects so you can just run through a loop of all the TextHelper's you have and draw/update them.

SpriteFont font;
List<TextHelper> TextList = new List<TextHelper>();
TextHelper text_topLeft;
TextHelper text_topCenter;
TextHelper text_topRight;

 

LoadContent(...) is a good place to create all of your text lines and we have to create our SpriteFont in here anyways so we'll keep it simple and in one place.

font = Content.Load<SpriteFont>("font");
//Default alignment is top-left so we don't assign it here
text_topLeft = new TextHelper(font, "TopLeft_FadeIn_5_Seconds",
    new Vector2(0, 0), 1f, Color.Red);
TextList.Add(text_topLeft);
text_topLeft.FadeIn(5);
text_topCenter = new TextHelper(font, "TopCenter",
    new Vector2(graphics.GraphicsDevice.Viewport.Width / 2, 0),
    TextAlignment.TopCenter, 1f, Color.White);
TextList.Add(text_topCenter);
text_topRight = new TextHelper(font, "TopRight_Scale_1.5",
    new Vector2(graphics.GraphicsDevice.Viewport.Width, 0),
    TextAlignment.TopRight, 1.5f, Color.Blue);
TextList.Add(text_topRight);

You'll have to either create/add your own SpriteFont or use one that you already have in that class. These are just some test instances so we can make sure it works. If you look at the first TextList.Add(...) line you'll see that I didn't include TextAlignment.(...). In this case it just defaults to TextAlignment.TopLeft.

In the Update(...) method, add the following if you're using the List of TextHelpers. Otherwise you can update each TextHelper individually.

foreach(TextHelper t in TextList)
    t.Update(gameTime);

Finally in our Draw(...) method just add these 2 lines and everything should work!

foreach (TextHelper t in TextList)
    t.Draw(spriteBatch);

 

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

(required)

No trackbacks yet.