MonoGame Tutorial: Building a 2D Game Using C#

Part 5 Drawing Images on the Screen

In Part 4, we loaded all of our image and sound assets into the content manager. Now that we have them loaded, let’s put them to use. We’ll start with the paddle. The paddle will be drawn near the bottom of the screen, and can move from the left to the right side of the screen. The player will try to hit the ball to keep it from falling off the bottom of the game screen.

The location where the ball contacts the paddle will determine the direction the ball will bounce. If it hits the left half of the paddle, it will be bounced back toward the left. And if it hits the right half, it will bounce back to the right. The farther the ball hits from the center of the paddle, the greater will be the angle of deflection.

We’ll create a class called Paddle to represent the paddle. In Visual Studio, right-click on the Bricks project and select Add and Class from the drop-down menus:
Add New Class
In the Add New Item Dialog, enter “Paddle.cs” for the class name, then click Add:
Add Paddle.cs File
In the “Paddle.cs” file, enter the following code:
[code language=”csharp” highlight=”6,7,8,9,15,16,17,18,19,20,21,22,23,24,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,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77″]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Bricks
{
class Paddle
{
public float X { get; set; } //x position of paddle on screen
public float Y { get; set; } //y position of paddle on screen
public float Width { get; set; } //width of paddle
public float Height { get; set; } //height of paddle
public float ScreenWidth { get; set; } //width of game screen

private Texture2D imgPaddle { get; set; } //cached image of the paddle
private SpriteBatch spriteBatch; //allows us to write on backbuffer when we need to draw self

public Paddle(float x, float y, float screenWidth, SpriteBatch spriteBatch, GameContent gameContent)
{
X = x;
Y = y;
imgPaddle = gameContent.imgPaddle;
Width = imgPaddle.Width;
Height = imgPaddle.Height;
this.spriteBatch = spriteBatch;
ScreenWidth = screenWidth;
}

public void Draw()
{
spriteBatch.Draw(imgPaddle, new Vector2(X, Y), null, Color.White, 0, new Vector2(0, 0), 1.0f, SpriteEffects.None, 0);
}

public void MoveLeft()
{
X = X – 5;
if (X <1)
{
X = 1;
}
}
public void MoveRight()
{
X = X + 5;
if ((X + Width) > ScreenWidth)
{
X = ScreenWidth – Width;
}
}

public void MoveTo(float x)
{
if (x>=0)
{
if (x < ScreenWidth-Width)
{
X = x;
}
else
{
X = ScreenWidth – Width;
}
}
else
{
if (x<0)
{
X = 0;
}
}
}
}
}
[/code]
The class properties are pretty basic. We have the X and Y coordinates of the paddle on the screen (with 0,0 being at the top left corner of the screen), and the Width and Height of the paddle in pixels. The ScreenWidth property has the width of the game field in pixels. We’ll use that to keep the paddle within the game play boundary. The imgPaddle field will contain the image of the paddle that we’ll draw on the screen. And finally, the spriteBatch field will be used to draw to an off screen buffer for eventual display to the screen. All of the fields are initialized in the constructor method.

We will call the Draw method from the Game1 Draw method. We’ll use the spriteBatch Draw method to write the image to the screen buffer for display:
[code language=”csharp” light=”true”]
spriteBatch.Draw(imgPaddle, new Vector2(X, Y), null, Color.White, 0, new Vector2(0, 0), 1.0f, SpriteEffects.None, 0);
[/code]
Let’s take a look at the arguments for the Draw method. The first argument is the image we want to draw. We saved a reference to the image we loaded in the GameContent class so we can draw it when requested. The next argument is the X,Y coordinates on the screen where the paddle is to be drawn. The next parameter is the portion of the image we want to draw. It is a rectangle (X,Y, Width, Height). We want to draw the whole image, so we left this parameter “null”. We could use this to clip the image if we wanted to.

The next parameter is the tint color. We specified “Color.White” which means we won’t apply a tint. We’ll use this parameter later to tint the game bricks. The next two fields are used if we want to rotate the image. We don’t want to, so we’ll use a rotation of “0”, and a rotation origin of “0,0”. The next field is the scale that we want to draw the image with. We are using a value of “1.0” which means we want it at 100%, or full size. If we used, say “.5”, the image would be scaled to 50% of the size from the file.

We aren’t using SpriteEffects, so we passed a value of “None”. This field can be used to flip the image horizontally or vertically. Finally, the last field is the layer we want to draw the image at. If we are drawing multiple images at the same coordinates, we can use this to determine the order in which the images are drawn. We won’t be using this for this tutorial.

We’ve also created three other methods. The MoveLeft method will move the paddle coordinates to the left by 5 pixels, making sure we don’t move beyond the playing field. The MoveRight method will move the paddle coordinates to the right by 5 pixels, also making sure we don’t move beyond the playing field. And finally, the MoveTo method will move the paddle to the specified coordinate. We won’t use these methods yet, but will need them in Part 7, when we add the logic to move the paddle to our game.

Now we need to create an instance of the Paddle class in “Game1.cs”. In the constructor method of the Game1 class add a new paddle field. We’ll also create two private variable to hold the screen height and width:
[code language=”csharp” highlight=”7,8,9″]
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
GameContent gameContent;

private Paddle paddle;
private int screenWidth = 0;
private int screenHeight = 0;
//rest of Game1.cs not shown for space considerations
[/code]
We’ll update our LoadContent method in “Game1.cs” to create our Paddle object, and also to set the game screen size. Add the highlighted lines below to Game1 LoadContent, and then we’ll describe what we’ve just added:
[code language=”csharp” highlight=”8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26″]
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;
graphics.ApplyChanges();

//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
}
[/code]
This method will now resize our game board and window to the size we want, and create our Paddle object instance. We are using the GraphicsAdapter Class to get the current screen size, and then changing it to 502×700 pixels. We then use the graphics object to change the game window size, and call the ApplyChanges method to have MonoGame update the window’s size. The next couple of lines will set the X,Y coordinates for the paddle to the middle of the screen, 100 pixels from the bottom.

Now that the paddle has logic to draw itself, we need to tell the paddle to draw itself whenever we get a draw event from MonoGame. We need to add the line to “Game1.cs” to call our new Draw Method. We also need to change the background color of the game board to Black. We’ll do that by changing the “GraphicsDevice.Clear(Color.CornflowerBlue)” statement to “GraphicsDevice.Clear(Color.Black)”. Also, add the spriteBatch.Begin(), paddle.Draw(), and spriteBatch.End() method calls:
[code language=”csharp” highlight=”3,6,7,8″]
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black); //Change the GraphicsDevice.Clear method to use Color.Black as shown here

// TODO: Add your drawing code here
spriteBatch.Begin();
paddle.Draw();
spriteBatch.End();
base.Draw(gameTime);
}

[/code]
Let’s talk about the spriteBatch object. We will be using this object to draw all of the objects to the screen. All of the spriteBatch.Draw method calls between the spriteBatch.Begin call and spriteBatch.End call, will cause the images to be written to an off screen memory buffer. The End call will cause that memory to be written to the screen. All of our Draw calls must be done between the pair of Begin and End calls.

Now click F5 to run the game, and you should see the game board is now black, and our paddle is drawn.

Let’s add the bricks that we’ll be smashing in our game. We’ll add a new Brick class for this. Right-click on the project Bricks in the solution explorer, and select Add and Class from the drop-down menus:
Add new Class
In the Add New Item Dialog, enter “Brick.cs” for the class name, then click Add:
Add Brick.cs file
Now, enter the following code into your “Brick.cs” file. We’ll talk about what you’ve added in a bit:
[code language=”csharp” highlight=”6,7,8,9,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42″]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Bricks
{
class Brick
{
public float X { get; set; } //x position of brick on screen
public float Y { get; set; } //y position of brick on screen
public float Width { get; set; } //width of brick
public float Height { get; set; } //height of brick
public bool Visible { get; set; } //does brick still exist?
private Color color;

private Texture2D imgBrick { get; set; } //cached image of the brick
private SpriteBatch spriteBatch; //allows us to write on backbuffer when we need to draw self

public Brick(float x, float y, Color color, SpriteBatch spriteBatch, GameContent gameContent)
{
X = x;
Y = y;
imgBrick = gameContent.imgBrick;
Width = imgBrick.Width;
Height = imgBrick.Height;
this.spriteBatch = spriteBatch;
Visible = true;
this.color = color;
}
public void Draw()
{
if (Visible)
{
spriteBatch.Draw(imgBrick, new Vector2(X, Y), null, color, 0, new Vector2(0, 0), 1.0f, SpriteEffects.None, 0);
}
}
}
}
[/code]
Okay, let’s take a look at what we added here. This is the code we’ll need to manage a single brick. The X and Y properties are the X and Y coordinates of the brick on the screen. The Height and Width properties give us the size of the brick. We’ll use the Visible property to determine if the brick has been destroyed or not. We’ll set this property to “false” after the ball destroys it. The Color property will be used to determine what color the brick should draw itself as. We’ll also store a reference to the brick image in the imgBrick property, and save a copy of spriteBatch, so we can draw the brick when requested. All of the properties will be set in the constructor methods from arguments that are passed.

The only other piece to this simple class is the Draw method, which will be called when the brick needs to be drawn. It will first check to see if the brick is visible, and if so, it will draw the brick using the spriteBatch.Draw method call. All of the parameters are the same as we’ve previously described in the paddle class, with one exception. In this call, we are making use of the color argument to tint the brick to different colors. If you took a look at the “brick.png” file, you would notice that it is black and white. As you will see when we draw them in the game, we show all of the colors of the rainbow. We do this by using a feature of MonoGame. The color argument will apply a tint to the image. A value of Color.White will tells MonoGame to apply no tint. Any other color value will cause that tint color to be applied to the image when drawn.

Our game won’t be terribly impressive with only one brick, so let build a wall of bricks. We’ll create a new class called Wall that will have seven rows of bricks that we can destroy. Right-click on the project Bricks in the solution explorer, and select Add and Class from the drop-down menus:
Add Wall.cs File
In the Add New Item Dialog, enter “Wall.cs” for the class name, then click Add:
Add Wall.cs
Now, enter the following code into your “Wall.cs” file. Then, we’ll talk about what it takes to build a wall:
[code language=”csharp” highlight=”6,7,8,9,15,16,17,18,19,20,21,22,23,24,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,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73″]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Bricks
{
class Wall
{
//We’ll have 7 rows, each with its own color
//there will be 10 bricks per row
//there will be 3 blank rows at top
//each brick is 50 x 16
public Brick[,] BrickWall { get; set; }

public Wall(float x, float y, SpriteBatch spriteBatch, GameContent gameContent)
{
BrickWall = new Brick[7, 10];
float brickX = x;
float brickY = y;
Color color = Color.White;
for (int i = 0; i < 7; i++)
{

switch (i)
{
case 0:
color = Color.Red;
break;
case 1:
color = Color.Orange;
break;
case 2:
color = Color.Yellow;
break;
case 3:
color = Color.Green;
break;
case 4:
color = Color.Blue;
break;
case 5:
color = Color.Indigo;
break;
case 6:
color = Color.Violet;
break;
}
brickY = y + i * (gameContent.imgBrick.Height + 1);

for (int j = 0; j < 10; j++)
{
brickX = x + j * (gameContent.imgBrick.Width);
Brick brick = new Brick(brickX, brickY, color, spriteBatch, gameContent);
BrickWall[i, j] = brick;
}
}
}
public void Draw()
{
for (int i = 0; i < 7; i++)
{
for (int j = 0; j < 10; j++)
{
BrickWall[i, j].Draw();
}
}
}
}
}
[/code]
Our wall will have 7 rows of 10 bricks. We’ve created an array of Brick objects called BrickWall to hold our bricks. Our constructor will pass the X and Y coordinates of the top left corner of the wall. Since we know the height and width of a single brick, our constructor will just increment the coordinates to lay out the bricks in a 7 x 10 grid. For each of the seven rows we assign a different drawing color.

Our Draw method is very simple, we just iterate through the brick array, and tell each brick to draw itself.

Now, we need to have our Game1 class create an instance of the wall, and have its Draw method call the Wall class Draw method.

In “Game1.cs”, add a new “wall” property:
[code language=”csharp” highlight=”8″]
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
GameContent gameContent;

private Paddle paddle;
private Wall wall;
//rest of class not shown to conserve space
[/code]
In the “Game1.cs” file, create a new instance of the Wall class, by adding the indicated line to your LoadContent method:
[code language=”csharp” highlight=”27″]
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;
graphics.ApplyChanges();

//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);
}
[/code]
Finally, we need to add tell the wall to draw itself. In the “Game1.cs” file, add a line to call wall.Draw()” as shown below:
[code language=”csharp” highlight=”7″]
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
// TODO: Add your drawing code here
spriteBatch.Begin();
paddle.Draw();
wall.Draw();
spriteBatch.End();
base.Draw(gameTime);
}
[/code]
Let’s run our game, by pressing F5. The game should now display both the paddle and the wall of bricks we just added.
Game with Paddle and Bricks
Our playing field is nearly complete. We are nearly ready to start adding some action to the game, but first, we need to add a game border. We’ll do that next, in Part 6.

13 thoughts on “MonoGame Tutorial: Building a 2D Game Using C#”

  1. I was looking for a quick tutorial to dive into MonoGame. Thank you! This one is perfect! It’s very easy to understand basic concepts of game making.

  2. Excellent tutorial . Although XNA is no longer supported by MicroSoft, it’s a good framework for 2D game development.

  3. Exactly what I was looking for! I heard that Axiom Verge was built with Monogame and instantly wanted to figure out what I could do with it as well. Thanks for setting me on the right track with this quick tutorial 🙂

  4. What a great tutorial! Very too the point with great code examples! Thoughts on doing another one on another game type?

  5. Great tutorial! This really helped me understand the basics of XNA/MonoGame; which in turn will help me get past a hurdle with something else I was attempting.

    Also, being the audio nerd that I am I couldn’t help but play around with the panning option. Using what I had already learned in the tutorial, is was simple enough to make the brick breaking sound pan more left or right as you get farther from the center. Fun stuff!

  6. The best XNA/MonoGame tutorial so far!!
    I`m so hyped about the game, that I would love to see how to make more levels with more difficulties.

    Great Job!

Leave a Reply

Your email address will not be published. Required fields are marked *