Get up to 80 % extra points for free! More info:

Lesson 1 - MonoGame Project Structure

In the introduction to the MonoGame, we introduced and installed the framework. Today, we're going to take a look at the structure of a MonoGame project and explain the basics of working with this framework.

I'd like to point out once again that this course is teaching the MonoGame framework, not the C# language itself. If you aren't familiar with the C# language, go through at least the first two C# courses and also the Files and I/O course.

We already have the MonoGame framework installed from the last time, so let's start Visual Studio and create a new MonoGame Cross Platform Desktop Project, which we'll name Robotris:

Start a new MonoGame project - Tetris From Scratch

If the name sounds similar to Tetris to you, you're right. The whole course will be focused on this particular game. We'll create the game itself and also the game menu, online score table, and the screen with the authors. You'll learn the basics of the framework and become capable of making any game :)

Project Structure

MonoGame projects have specific structure. In the new solution you'll find the x64/ and x86/ folders, which, together with the *.dylib files, ensure the project to work cross-platform. For our tutorial, the most important thing is the Game1.cs file, which contains the main game logic, and the Content/ folder, which contains all the assets (images, sounds and music). We add the assets into this folder using the MonoGame Pipeline Tool.

First, let's look at the Game1.cs class that the Visual Studio generated for us, and describe its code:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Robotris
{
    /// <summary>
    /// This is the main type for your game.
    /// </summary>
    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        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
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// game-specific content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here

            base.Draw(gameTime);
        }
    }
}

Namespaces

First there are several namespaces declared. Here we can see that there's no MonoGame namespace and that everything is provided by the Microsoft.Xna.Framework namespace. This is because MonoGame is based on the XNA framework, and the developers decided to use the original namespace. That's all about the namespaces, and now let's move on to the Game1 class.

The Game Class

We'll rename the class to RobotrisGame by moving the cursor on Game1, pressing F2, and confirming the following dialog. We can see that the class inherits from the Microsoft.Xna.Framework.Game class.

We have 2 fields declared - graphics and spriteBatch.

  • graphics is a GraphicsDeviceManager instance. It provides methods to resize the game window, switch to the fullscreen mode, and so on.
  • spriteBatch is a SpriteBatch instance. It provides the sprite functionality. We have a new term here - sprite. Sprite is simply an image. It's a key element of all 2D games, since they're used for basically everything, like the game background, the game characters, or fonts (each letter is a single sprite). Sprites can also be animated.

The Constructor and Initialize() Method

Let's move on to the constructor. Here the graphics field is initialized, and the root folder for the Content is also set. We already know that it's the folder in which our game data will be stored.

In addition to the constructor, we also have the Initialize() method. This may be confusing, especially since MonoGame doesn't tell us when to use the constructor to initialize and when to use the Initialize() method.

The difference is that the constructor is called when the game instance (or a game component instance, which we'll explain later) is created, and it should set up the game or the component. We won't add anything new here for the game. Another purpose of the constructor is to pass dependencies (we'll need them for the components that the game will be made from).

The Initialize() method is then used to load the non-content data (that means, no sprites, sounds, or music), such as map files. We'll also do all the game initialization here, create all the necessary objects, set up properties and so on.

The Initialize() method already has an embedded line of code:

base.Initialize();

It takes care of calling the Initialize() method of all the game components. We'll say more about the components later. Similar line can be also found in other methods of the class.

LoadContent()

The class also have the LoadContent() method which loads the game content such as sprites, sounds, and music. We can see that the spriteBatch is also created here. At the end of the method we can write custom behavior, which we need to run after the content is loaded. For example, we can't get the height of a font that hasn't been loaded yet, so the code would go to the LoadContent() method, instead of the Initialize() method.

The UnloadContent() method is called when the game is about to quit. But because we'll always use Content (which is a game property with a ContentManager instance) which releases the resources automatically, the method isn't important to us.

The following two methods are the most important ones.

Update()

The Update() method contains the real-time logic, that is, everything that is processed in real time. This is usually the keyboard, mouse or other controls input, collisions, and objects movement. Generally, we implement reaction to events, objects movement, or even animations here. The method is called 60 times per second. If we move a game character in this method by 1, he'd walk with the same speed even on a faster computer. We can set a different interval as a decimal number less than 1 using the TargetElapsedTime property, but we won't need that. It's important to know that MonoGame will optimize automatically, so if the computer isn't fast enough, the game will compensate it by omitting some frames.

In the method parameter we get a GameTime instance that contains the game time. It has two useful properties:

  • ElapsedGameTime - A TimeSpan of the elapsed time since the last update.
  • TotalGameTime - A TimeSpan of the total elapsed time since the game start.

Thanks to these properties, we can work with the real time inside the Update() method, do something every second, minute and so on. We'll show everything later in this course.

Another property of the gameTime is IsRunningSlowly, which is true whenever the game is lagging and MonoGame omits to render some of the frames. That gives us the opportunity to react to such situations, but we won't create such advanced games that would need it yet :)

The method already contains behavior that quits the game when the Back button on the gamepad is pressed. This is very important code that makes it able to quit the game on XBox if we run it there. Among other things, we can see that the game is quit using the Exit() method.

Draw()

The Draw() method takes care of the game rendering (as the name suggests). Rendering is done by calling methods of a SpriteBatch instance. The graphics device must be cleared before rendering each frame, to avoid having there objects rendered in the last frame. This is achieved using the Clear() method and the blue color (the color actually doesn't matter if the game has a custom background). Here we meet the Color structure, which MonoGame uses to represent colors. It has several static methods that return its instance set to specific colors.

The Game Loop

It's important to know how the game works under the hood. The game methods are called in a loop called game loop in the following order:

  1. Initialize()
  2. LoadContent()
  3. Update()
  4. Draw()
  5. UnloadContent()

The Update() and Draw() methods are called repeatedly until the game is quit. The loop explained in a flowchart would look like this:

Game loop in MonoGame - Tetris From Scratch

In the next lesson, Adding Content in MonoGame, we'll add content into our project, such as sprites, fonts, sounds and music :)


 

Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.

Download

By downloading the following file, you agree to the license terms

Downloaded 27x (141.93 kB)
Application includes source codes

 

All articles in this section
Tetris From Scratch
Skip article
(not recommended)
Adding Content in MonoGame
Article has been written for you by David Capka Hartinger
Avatar
User rating:
7 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities