In the first post in this series we looked at getting the environment set up, and created a game based on the MonoGame (XAML) project template. Of course this game didn’t do anything except show a blank game surface but sometimes the first step is the hardest one to take. Over the past few years I’ve tried to think of ways to demystify game development since I frequently hear very experienced developers say that they don’t think they could actually write a game. Game development has a bit of a different way of thinking about it, especially compared to what .NET developers typically do on a daily basis.
Let’s think a bit about creating a “Hello World” in WPF. Typically what you would do is drop a button on the window, handle a click event, and pop up a message box or change some text. This is event-based programming, you wait for something to happen and then act upon it.
In game programming, you often have things going on while you’re waiting for input. This could involve animations, processing the current position of objects based on physics, checking the network for the other player’s actions, and a myriad of other things. The most common model for game development is to do loop-based programming, specifically a game loop. A game loop is something that executes repeatedly on a timer for the life of the game. In XNA and MonoGame, there are actually what can be considered two game loops, the Update loop and the Draw loop. The Update loop calls the Update method repeatedly, and is where you would do things like check for input, kick off sound effects, call into the physics engine, etc. The Draw loop calls the Draw method once per frame for when the screen needs to be updated, and you need to draw everything every frame. If you don’t draw something when the Draw method is called, even if you drew it last time, it won’t show up. This may seem wasteful, but when there is a lot going on, let’s say a bunch of particle effects and a lot of things moving around, it would take more processing effort to try to keep track of where all of the elements are in a “visual tree” like WPF, Silverlight, WinForms or other similar technology uses.
I like to simplify game development in MonoGame or XNA into the following tasks:
- Draw Stuff
- Handle Input
- Play Sounds
When you think of it this way, none of those tasks seems too daunting does it? I’d actually argue that getting familiar with event based programming is a lot more complicated and harder to grasp. If you took a complete novice to programming and tried to explain both types of programming I’d argue that game programming would have a shorter ramp-up time.
So let’s get start by drawing stuff.
Content in XNA and MonoGame
Since MonoGame is modeled after XNA and they want to make the bar as low as possible to port an XNA game to MonoGame, they have made a huge effort to be compatible with the content format that XNA uses. Content items in XNA are called “assets” and are packaged with the game as files with an XNB file extension. There is a special project type in XNA called a Content Project, and at compile time any assets in the content project are compiled into this standard XNB format that the XNA runtime understands. This simplifies the runtime significantly since it doesn’t need to know about all of the various content formats that XNA can accept. Take for example image formats. XNA content projects can accept BMP, JPG, TGA ,PNG and DDS out of the box, and you could even write your own custom content importer to support other formats. At run time, XNA only has to deal with the image format that all of these others are converted to at compile time.
Now even though the MonoGame runtime knows how to handle XNB files, there is nothing built into MonoGame currently to generate these files, although I think they plan on supporting this in the future. This means that you need to generate these XNB files using an XNA Content Project, and then add these XNB files to your MonoGame project.
This is where things get a little tricky for MonoGame on Windows 8. Since Windows 8 Store development doesn’t support XNA, Visual Studio 2012 doesn’t ship with the XNA project templates. In general it looks like Microsoft is moving away from XNA on all of its platforms. But all is not lost. One option is to install Visual Studio 2010 (or Express) side by side with Visual Studio 2012 and install XNA Game Studio 4.0 and build your content there, and then add it to your Visual Studio 2012 based MonoGame project. This isn’t ideal since you have to switch back and forth and have two solution files.
I’ve come up with a better idea. Since Windows Phone 8 can run Windows Phone 7.1 apps and games, and since Microsoft is still supporting developing XNA games for Windows Phone 7.1 in their Windows Phone 8 SDK, and since the Windows Phone 8 SDK runs on Visual Studio 2012, you can install the Windows Phone 8 SDK and then use the XNA Content Project support that ships with it to build your XNB files! This is a little tricky to set up but once it’s set up it’s pretty sweet so stick with me through this.
Download and install the free Windows Phone 8 SDK
Now, open up the solution we started in the last post. If you skipped it, you can get it here.
Add a new project to the solution of type Empty Content Project. You can find this under C#->XNA Game Studio 4.0. We’ll take the default name of “Content1”
Add a new project of type Windows Phone Game Library (4.0). We’ll call this “DummyLibrary” since its only use is so that we can harvest the XNB files.
Delete the Class1.cs file from the DummyLibrary project, we don’t need it.
Right click on the DummyLibrary project node in the Solution Explorer. Select Add Content Reference…
Now go to Solution Properties and Project Dependencies. For MyFirstGame, select that it depends on DummyLibrary. This will ensure that the DummyLibrary project (and therefore the XNB files) will be compiled before the game project.
Now to actually add the content. We want an image we can draw. Let’s borrow one from the XNA Platformer Starter kit.
Click on this image which will open it up in a new window and then save it as Run.png.
Now in the Content1 project, Select Add Existing Item and select Run.png.
Build the solution. This will generate the XNB file we need.
Add a new folder named Content to the MyFirstGame project.
On the Content folder, select Add Existing Item. Navigate to the DummyLibrary\bin\Windows Phone\Debug\Content1 folder and make sure the filter is set to All Items. Select the Run.xnb file, and then Add As Link.
By using Add As Link instead of Add, when this file changes the MyFirstGame project will automatically pick up these changes.
Now one last thing. Since Visual Studio doesn’t know what to do with an XNB file added to a project, it sets its Build Action to None, so you need to manually set the Build Action of Run.xnb to Content instead.
Ok so now we’re finally able to use this content. In the future to add more content, which we’ll see in a later post, all you’ll need to do is add it to the content project, do a build, and then go to the Content folder of your game, and add the XNB file “As Link”, and set the Build Action to Content.
This series of posts will focus strictly on 2D games in MonoGame. The main reason for this is that the level of complexity for game development goes up exponentially once you get into 3D. Also some of the most popular games ever are 2D games, so your best best is probably to build one of these first before moving on to 3D. That being said, MonoGame has good support for most of XNA’s 3D APIs, and if you run into anything it doesn’t support you can put in a request for them to add it.
When writing 2D games in XNA or MonoGame, you are actually still using 3D, but there is an abstraction layer put on top of this that makes it feel like a 2D surface. This means if you wanted to, you could mix 2D and 3D graphics and this is commonly done for things like a heads up display (score, health, etc).
To make writing 2D games easier, XNA (and consequently MonoGame) provides a SpriteBatch class. The SpriteBatch class provides APIs for drawing images and text, so you will be using it extensively in your 2D games.
If you look in the Game1.cs file, you’ll see that the default template declares a SpriteBatch object for you.
And in the LoadContent method, it is instantiated:
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
Notice the comment says “draw textures”. Bitmaps or images in XNA are referred to as Textures, and for the textures that we’re going to draw with the SpriteBatch we need to load them into Texture2D objects. So let’s declare a Texture2D as a field in the Game1 class:
and then in the LoadContent method, we’ll instantiate it by telling the content manager to load it.
player = Content.Load<Texture2D>("Run");
By default, the content manager looks in the Content folder, and the asset name is the file name without the extension by default. If you wanted a different asset name (which would also change the file name of the XNB file) you can change this in the content project in the item’s property pane.
Note that in XNA and in MonoGame for Windows 8, asset names are not case sensitive, so “run”, and “Run” point to the same file. On MonoGame for Android, this is not the case so be careful.
Now to draw this texture. As the “batch” in the name SpriteBatch implies, drawing is done as a batch. This means you draw one or many things, and then tell it you’re done, and then at that point it actually goes off and does the drawing. So before you begin drawing, you need to tell SpriteBatch to Begin the batch, and then once done, call End. If you don’t do this nothing will draw and you’ll likely get runtime errors.
In the Draw method, add the following:
_spriteBatch.Draw(player, Vector2.Zero, Color.White);
This draws the texture object we called “player” at position 0,0 on the screen (top left corner) with a color of white. In the next post we’ll get more into vectors and colors along with other fun things SpriteBatch can do, but for now let’s just run this and you should see the following:
Click here for the solution as of this point.
That’s it for now, tune in next time for more fun with SpriteBatch.