Part 10 – Drawing Text
Our game is somewhat playable now, but it doesn’t show the Score, or the number of Balls remaining, and we don’t haven’t Start or Game Over messages displayed on the screen. In this final part, we’ll add these finishing touches. As we mentioned earlier, DirectX (which MonoGame uses) doesn’t directly have a mechanism for drawing text. Text is actually drawn on the screen like any other image. Bitmap fonts have an image for each character in the character set, and whenever we tell MonoGame to write text to the screen, it is actually drawing those images for us. That’s why we had to add the spriteFont in Part 3. That caused the pipeline tool to generate the necessary images. The SpriteBatch class has a “DrawString” method that we’ll use to draw text on the screen.
One piece of information that we need to display, is how many balls the user still has available to play. Games like this usually show an icon for the ball along with a number, so that’s what we’ll do here. In our “Game1.cs” file add the following line to add another ball object to our game:
[code language=”csharp” highlight=”11″]
public class Game1 : Game
private Paddle paddle;
private Wall wall;
private GameBorder gameBorder;
private Ball ball;
private Ball staticBall; //used to draw image next to remaining ball count at top of screen
//rest of Game1 class not shown to save space
In the same file, in the LoadContent method, add the lines to create the instance of the ball we just added:
[code language=”csharp” highlight=”30,31,32,33,34″]
protected override void LoadContent()
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
gameContent = new GameContent(Content);
screenWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
screenHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
//set game to 502×700 or screen max if smaller
if (screenWidth >= 502)
screenWidth = 502;
if (screenHeight >= 700)
screenHeight = 700;
graphics.PreferredBackBufferWidth = screenWidth;
graphics.PreferredBackBufferHeight = screenHeight;
//create game objects
int paddleX = (screenWidth – gameContent.imgPaddle.Width) / 2; //we’ll center the paddle on the screen to start
int paddleY = screenHeight – 100; //paddle will be 100 pixels from the bottom of the screen
paddle = new Paddle(paddleX, paddleY, screenWidth, spriteBatch, gameContent); // create the game paddle
wall = new Wall(1, 50, spriteBatch, gameContent);
gameBorder = new GameBorder(screenWidth, screenHeight, spriteBatch, gameContent);
ball = new Ball(screenWidth, screenHeight, spriteBatch, gameContent);
staticBall = new Ball(screenWidth, screenHeight, spriteBatch, gameContent);
staticBall.X = 25;
staticBall.Y = 25;
staticBall.Visible = true;
staticBall.UseRotation = false;
We set the UseRotation property to false because we don’t want this ball spinning. Now let’s add our text messages to the game. We’ll need the following:
- Number of balls remaining
- Start Game Message
- Game Over Message
These will all be added to the Draw method in “Game1.cs”. Add the indicated lines to the Draw method. We’ll discuss what they do in a bit:
[code language=”csharp” highlight=”23,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50″]
protected override void Draw(GameTime gameTime)
// TODO: Add your drawing code here
bool inPlay = ball.Move(wall, paddle);
readyToServeBall = true;
string scoreMsg = "Score: " + ball.Score.ToString("00000");
Vector2 space = gameContent.labelFont.MeasureString(scoreMsg);
spriteBatch.DrawString(gameContent.labelFont, scoreMsg, new Vector2((screenWidth – space.X) / 2, screenHeight – 40), Color.White);
if (ball.bricksCleared >= 70)
ball.Visible = false;
ball.bricksCleared = 0;
wall = new Wall(1, 50, spriteBatch, gameContent);
readyToServeBall = true;
if (ballsRemaining > 0)
string startMsg = "Press <Space> or Click Mouse to Start";
Vector2 startSpace = gameContent.labelFont.MeasureString(startMsg);
spriteBatch.DrawString(gameContent.labelFont, startMsg, new Vector2((screenWidth – startSpace.X) / 2, screenHeight / 2), Color.White);
string endMsg = "Game Over";
Vector2 endSpace = gameContent.labelFont.MeasureString(endMsg);
spriteBatch.DrawString(gameContent.labelFont, endMsg, new Vector2((screenWidth – endSpace.X) / 2, screenHeight / 2), Color.White);
spriteBatch.DrawString(gameContent.labelFont, ballsRemaining.ToString(), new Vector2(40, 10), Color.White);
The first line you added tells the new “Balls Remaining” staticBall to draw itself: staticBall.Draw. Next, we create the score message by taking a string literal “Score: “, and concatenating a leading-zero string with the game score from the Ball class. We want to center this on the screen, so we use the SpriteFont MeasureString method to determine how much space this string will take on the screen. We then call the SpriteBatch DrawString method. The first argument we pass is the font we want to use: labelFont. Next, we pass the string to be displayed, our score.
The next parameter is a vector containing the X and Y coordinates where the text is to be drawn. We compute the X coordinate using the width of the screen minus the length of the string displayed and dividing by two, to center it on the screen, and position it 40 pixels from the bottom of the screen. The final parameter is the color that should be used to draw text, in this case white.
We’ve also added some game logic to check if all 70 bricks have been cleared. If so, we’ll hide the ball, and set up for a new game level by creating a new wall, and setting a flag indicating that we are ready to serve a ball.
Next, if we are ready to serve a ball, and there is at least one game ball left to play, we display the game start message. If there are no balls left, we display the game over message.
Finally, we display the balls remaining count next to the ball icon at the top of the screen.
Phew, we’re done! Press F5 and you should see the messages and be able to play the game in all of its glory!
We could add more polish, like displaying the game level, or speeding up the ball on higher levels, or shrinking the size of the paddle at higher levels, like the old arcade game does. I’ll leave that as an exercise for you, if you want to fiddle with this further.
If you look back at what we’ve done in this tutorial, you’ll see that we covered most of the basics you will need to create your own 2D game. We learned how to add and build assets so that MonoGame can consume them. We learned how to load assets using the content manager. We learned how to draw images and play sounds. We learned how to draw lines and rectangles on the screen. We learned how to draw text on the screen and get mouse and keyboard inputs. And, we learned how to move and rotate images.
I hope you found this tutorial useful. If you did, please leave a comment and let me know. If you didn’t like it, please leave me a comment too, and tell me what I could do better. Also, let me know if there are other topics you’d like me to cover. I’m always looking for new ideas.
I hope this tutorial helped, and good luck with your gaming projects!
Here are the attributions for the sound file sources:
- StartSound.wav — http://soundbible.com/419-Tiny-Button-Push.html
- WallBounceSound.wav — http://soundbible.com/2084-Glass-Ping.htm
- BrickSound.wav — http://soundbible.com/1987-Rockslide-Small.html
- MissSound.wav — http://soundbible.com/1581-Buzz.html
- PaddleBounceSound.wav — http://soundbible.com/1343-Jump.html
Copyright (c) 2017 David Visti
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE