An XNA Primer Just For You!

Posted by DesertFox on Aug. 3, 2010, 3:59 p.m.

Hey people, thinking back, I've recently seen some comments over how people wanting to migrate from GM to something more robust are having a bit of difficulty, so I'm here to say…

Don't worry, Be happy!

This blog will explain the very baaaaasics of XNA and C# programming. More complicated stuff, like data structures and whatnot, you'll have to learn yourself - its really easy though. In fact, the first time I worked with XNA was when my professor said to the class "You have 30 minutes to make and display a keyboard-controlled player" - and we did, without any problem.

Instructions follow, and my code example is linked at the bottom, as well as here. So lets begin!

Step 1

Install C# Visual Studio if you haven't, and grab the latest version of XNA, and install that as well. If you cannot pass this step, kindly ask your nearest administrator to bonk you on your head. Once this is done, proceed to step 3. Or maybe step 2, if you want to do it properly and learn a little.

Step 2

Open up Visual Studio, and choose Create New Project. Select XNA Game Studio 3.1 as the project type, and Windows Game (3.1) as the project template. These templates are for the easy creation of different sorts of XNA projects, but by and large, you can ignore them for now. Name your project "Tutorial".

So now you should be able to see a large pane of code, starting with "using blah blah blah" for a bit. Those are namespace declarations, you can mostly ignore them until I say not to. On your right, you should see "Solution Explorer" with a drop-tree that includes "properties", "references", "Content", "Game.ico", etc. This is, in essence, your file manager. The first thing I'd do is rename Game1.cs to something more suitable - I usually choose "Main.cs" as this is the main runfile - "Program.cs" just creates Game1/Main and has it run. This is completely optional, however, for the rest of this tutorial, I will be referring to Game1/Main as "the main file" or "Main". If you choose to rename the file, Visual Studio will ask you if you wish to have it rename all references - say yes. What this does is it looks through the project and changes all of the names for you - a handy feature if you decide you want to rename a class/object and don't want to do find-replace.

Step 3

Run the program - you can do this by hitting F5, or Debug -> Start Debugging - it will automatically compile. A screen will pop up with a pretty shade of blue. Congratulations! You have the very core of a game! I mean, you can't DO anything, but still…

Now, lets look at the code inside of Main. Skim over everything. First thing that you will notice is that there is actually not too much code - just an absolute ton of comments - comments which are helpful. I'll run through each item anyway though.

First up is all of that "using blah blah blah" crap. What is this? Its namespace references. I won't go too deep into this, except to say that if you want to use something that you didn't write, you have to say it… Which is easy. You literally type "using Namespace.InnerNamespace.Etc. Which is easy. In this case, we are using:

using System, which is a bunch of core stuff

using System.Collections.Generic which is your data structures - Dictionaries and List

using System.Linq, which you can ignore because it has to do with queries

using Microsoft.XNA.Framework and friends, this is what you want - everything required to make games!

After that, you see "namespace Tutorial". This is just defining your own namespace. So what the hell is a namespace? Well, in short, its what it says - its a space, defined by a name. Its a collection of code. Think of it as a folder, with classes inside, with you being in the folder of "Tutorial". To access other folders, and thus the code classes inside them, you have to type "using Otherfolder;". Of course, you can instead access the code by typing Otherfolder.DesiredClass in your actual code, but if you need to use DesiredClass a lot, its better to do the "using" thing. Enough of namespaces!

After that, you see

Quote: CODE
public class Main : Microsoft.Xna.Framework.Game{

This is just saying "hey I'm writing my own class Main and it inherits from Microsoft….Game". Public means that any other class can see it, which is useful if you want to be able to use it.

After that you see

Quote: CODE
GraphicsDeviceManager graphics;

SpriteBatch spriteBatch;

So what are these? These are variables. Ignore these for now - feel free to add your own random variables though, if you know your primitives.

Aaaaand after that:

Quote: CODE
public Main()

{

graphics = new GraphicsDeviceManager(this);

Content.RootDirectory = "Content";

}

But didn't we already dooooo the public main? Sorta… We did public class Main, which said "Hey I'm making a class". This is different though. That was like right clicking and selecting "New Object". This is more like the Create Event. Anything in here happens when you make the object.nIt by default has no parameters, which is fine. Here we fill in one of the two variables we saw earlier - graphics! Its what it says it is - it is a graphics device manager, which means that it handles stuff like window position and size, etc. Content, if you notice, relates to the Content folder over in the Solution Explorer.

Following this is

Quote: CODE
protected override void Initialize()

{

base.Initialize();

}

protected override void LoadContent()

{

spriteBatch = new SpriteBatch(GraphicsDevice);

}

protected override void UnloadContent()

{

}

This is a bit more specific to the Main class . I stripped the comments because you already have them if you are actually following along, but they are pretty self-explanatory. These happen after the Main is created. Sort of like a post-creation event. Be forewarned, LoadContent actually happens before Initialize. UnloadContent happens when the game closes.

Initialize is for creating whatever objects you need to start your game, and you can use LoadContent to load your graphics (you can do it elsewhere later, but you can't do it before). A quick note about the "base.Initialize" - that is just a way of calling a base function. Main is a new class, so we write our own function for Initialize, but we still want it to do the stuff it's parent does in Initialize, so we use "base".

In LoadContent, "spriteBatch" is the second variable I told you to temporarily ignore. It has your much-desired "draw" methods. I'll come to that later - lets examine this now:

Quote: CODE
protected override void Update(GameTime gameTime)

{

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)

this.Exit();

base.Update(gameTime);

}

protected override void Draw(GameTime gameTime)

{

GraphicsDevice.Clear(Color.CornflowerBlue);

base.Draw(gameTime);

}

Hey, this looks important! Update and Draw! I bet you can guess what they do! First off, both have GameTime gameTime as parameters - that means a GameTime is passed to them, telling how much time has passed - useful, but not needed here.

Now, here's something I find funny. The bit in Update about "if(GamePad.etc) then this.Exit()". That only actually works if you have an XBox controller plugged in. Feel free to delete that line - we'll replace it really soon! Aside from that Update just calls base.Update - which if you recall, just means that it is calling it's parent's version of update.

Now onto Draw! Here we see "GraphicsDevice.Clear(Color.CornflowerBlue);". This is the reason the window is filled with blue - we are clearing the screen to that color. Feel free to change it to any other color. Try Color.Lime. To see a list of possibilities, just type "Color." and a list should pop up - use Up/Down to change selection, and tab to complete. Also, again you see "base.something".

Well, this tutorial is getting quite large, so…

TAKE A 10 MINUTE BREAK

Okay, back. Time for

Step 4

Now that we are done with explaining things, lets do some code.

Go over to the Solution explorer, and right-click anywhere, except on Properties, References, or Content. A giant menu will pop up with way too many options… somewhere in the middle you will see "Add" - hover over it, and a submenu pops up. Choose "New Class" which is at the bottom. A window will pop up with a bunch of possibilities! If it isn't already selected, find and select the "Class" template. Now, at the bottom, choose your class name. For the purposes of this tutorial, name it "Mover". The file should be created and automatically open, looking something like this…

Quote: CODE
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace Tutorial

{

class Mover

{

}

}

Congratulations, you have a game object. It is really just a class without anything inside of it, but it will be useful. Hey, look! It's that namespace again! Ignore it! Now, do you remember earlier that bit that was like the Create Event? Its not here! Because you need to write it! So lets do that! But before we do, an object is useless without information! This is a mover, and it has to move! For it to move, it has to have a position! So we have to make an X and a Y!

Inside of the class Mover brackets, put "public float X, Y;". Viola! But I'm not swimming! So whats with the floating? Float stands for floating-point number, which is basically a number that can have a decimal point. 1.5426 is a floating point number. When dealing with pure integers (1,2,3,etc) use int. If you are having difficulty, read this: http://en.wikipedia.org/wiki/Primitive_data_type#Specific_primitive_data_types - Note that for C# integers, byte is 8 bits, short is 16 bits, int is 32 bits (not 16) and long is 64 bits (not 32) - float and int are what you will use mostly.

Now we get to make our Create Event… FINALLY. In real-world programming, the Create Event is known as a "constructor" - it constructs the object!

Quote: CODE
public float X, Y;

public Mover(float startX, float startY)

{

X = startX;

Y = startY;

}

You can see the X and Y above the constructor, and you can see that the constructor has two arguments - startX and startY. You can also see that we assign the values of them to X and Y.

Now lets make our object's Update and Draw functions.

Quote: CODE

public void Update()

{

}

public void Draw()

{

}

Just a quick refresh - public means that other objects/classes/code can see and use these functions. But something new! Well, not new. It was in Main, but I chose to not point it out. Void. public void Update. public void Draw. What is this? Its effectively a reminder of your choice to sell your soul to the devil for programming prowess, a reminder of the void that awaits you! Actually, its just a return type. "void" means you don't return anything. You should remember "return" from GM. If not, return is the result of calling a function - a value returned for use. Math.Sqrt(16) return 4. We aren't returning anything, hence void.

But stop and think for a moment. We want this object to move when we press the arrows, and we want it to draw. Here's where all that stuff about namespaces is used. In XNA, all of the stuff to handle input and graphics are in the namespaces Microsoft.Xna.Framework.Input & Microsoft.Xna.Framework.Graphics respectively. So at the top of the mover file, use them! Like this!

Quote: CODE
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Input;

using Microsoft.Xna.Framework.Graphics;

namespace Tutorial

{

I also added using the generic framework namespace for something later on… Now back to Update! Now that we are "using" the input namespace. we can do stuff like this:

Quote: CODE
KeyboardState keyboard = Keyboard.GetState();

if (keyboard.IsKeyDown(Keys.Space))

Console.WriteLine("Hey space is down!");

KeyboardState is the state of the keyboard. You get it by calling Keyboard.GetState() which basically is like screaming "Hey keyboard, what are you doing?" We have to do this every frame, because it doesn't automatically update with the keyboard. The benefit of this is that you can save the last Update's keyboardstate, and compare the two.

After that, you see something gloriously simple. Its pretty much human readable. if (keyboard.IsKeyDown(Keys.Space)). If you wanted to check the arrows, instead of Keys.Space, you use Keys.Up, Keys.Down, etc… Console.WriteLine just writes a debug statement to the console in Visual Studio - it should be below your code under a tab labelled "output".

So see if you can figure out how to make it move, yourself. You know how to get keypresses! Highlight below for an answer!

if (keyboard.IsKeyDown(Keys.Left))

X -= 1;

if (keyboard.IsKeyDown(Keys.Right))

X += 1;

if (keyboard.IsKeyDown(Keys.Up))

Y -= 1;

if (keyboard.IsKeyDown(Keys.Down))

Y += 1;

You can use the same method in Main Update to make Esc quit the program, using this.Exit();

Now we want to draw our Mover, but alas, we have no sprites! Lets make some!

Step 5

First, we need a Texture to draw in the Draw method, so put "public Texture2D sprite;" with the other variable declarations in Mover. A Texture2D is that - a texture! A sprite! An image! Now, we are going to fill this in in Main.

Go back to Main. Remember when I said that LoadContent was used to… load content? Specifically images/textures? Lets do that shall we? Make a small block, 32x32 in MSPaint. Spend hours agonizing over the details. Or dont! Save it as "mover.png" on your desktop, or anywhere! Now, there are two ways to add it to your project. Right click on "Content" in Solutions Explorer, select Add->Existing Item. Or simply drag the file from your desktop to the content folder in Solutions Explorer. Both work.

Now, the mover - alas, we have not created one! Maybe I'm just lazy, a bad teacher, or possibly I was hoping one of you would notice this! We must first create one. So add a variable!

Quote: CODE
Mover player;

Put it at the top with SpriteBatch spriteBatch, and GraphicsDeviceManager graphics. Small infobyte - if you had made a folder named "Objects" and created the Mover file inside THAT, it would be in the namespace "Tutorial.Objects", and to access it in Main, you'd have to type Objects.Mover instead of Mover - unless you had typed "using Tutorial.Objects;"

Now, go to LoadContent in Main, and add this.

Quote: CODE
player = new Mover();

Hey! Error! Because it needs arguments! Specifically, its starting X and starting Y! Change it to this:

Quote: CODE
player = new Mover(200,200);

and in Update/Draw, add respectively:

Quote: CODE
player.Update();
Quote: CODE
player.Draw();

Now, back to LoadContent - after you create the player mover, add this:

Quote: CODE
player.sprite = Content.Load<Texture2D>("mover");

Content is a useful thing - it allows for easy loading of many types of resources - simply with a single call. In this case, it goes like this: "Content" - hey content folder! "Load<Texture2D>" - we want to load something! It has to be a texture! '("mover")' - the file is named "mover". You don't need to add the file extension, as when it compiles all resources are also compiled to a specific type. You can load resources that aren't in the Content folder, but you have to specify the full relative path including the extension. But we don't need that right now.

So what we've done so far is created the Main, created a mover object, given it controls, and given it a texture! So lets draw this thing!

To draw something with spritebatch, you first have to do a few things. You have to Begin, and you have to End. I'm not going to explain why, suffice to say that it involves readying surface targets and whatnot. So! In Main Draw, do this!

Quote: CODE
spriteBatch.Begin();

player.Draw();

spriteBatch.End();

Now! Onwards to the Mover object! Lets draw this texture! But wait! Texture2D doesn't seem to have a way of drawing itself! Because it doesn't! The SpriteBatch does! In other words, to draw the Texture2D, we need to use spriteBatch, but it's in Main, not in mover. Solution - add the spriteBatch as an argument to Mover Draw.

Quote: CODE
public void Draw(SpriteBatch spriteBatch)…

And pass it in Main Draw

Quote: CODE
player.Draw(spriteBatch)

Now, back to Mover Draw - we are almost finished! How do we draw the texture using a SpriteBatch? Well… Start typing "spriteBatch.Draw(" and then stop - a nice helpful tooltip will pop up with all of the possible ways to draw things. Remember when we "used" the Microsoft.Xna.Framework namespace? It contains a few classes we need - specifically the Vector2 class and the Rectangle class.

This is because when we draw the texture, it needs information like position, size, offset, etc. It gets more complicated if you want rotation, scaling, or to do spritesheets. For the sake of simplicity, we will use the easiest version of SpriteBatch.Draw - the one that only needs a Texture2D, a Vector2 position, and a draw Color. For those who don't know, a vector is just numbers - think of it as something containing coordinates - a Vector2 is a coordinate in 2D space - XY, a Vector3 is a coordinate in 3D space - XYZ, etc. Sidenote, SpriteBatch.Draw is what is called an 'overloaded function' - meaning it has a bunch of different sets of possible arguments, leading to different behaviors! Go take a look at them if you want to explore!

So! Inside Mover Draw, put this!

quote=CODE]Vector2 pos = new Vector2(X, Y);

spriteBatch.Draw(sprite, pos, Color.White);

And run! If you followed my directions, you should have a square that moves around when you press the keyboard arrows! If you didn't, it probably won't work! Because of that, I've provided my code project here!

——————————————————

I'd like to make a few notes now. This is by and far not a complete tutorial over all that C#/XNA has to offer. Its a quick primer at best. I've also neglected a few things for simplicity.

*"WAAAAAAAAUGH I want animated sprites!" - animating a spritesheet is a lot more complicated and requires writing your own class to handle it. It also requires a better understanding SpriteBatch Draw's overloaded functions. That would be a complete tutorial of its own, albeit shorter than this one.

*"WAAAAAAAAUGH I want to draw rotated, stretched, scaled sprites!" - I said earlier, look up SpriteBatch Draw's more complicated overloads. Also, read above.

*"OHNO I NEED MORE THAN ONE MOVER - In this case, I handled sprites in the simplest fashion - and in a way that doesn't allow for re-using sprites among multiple Movers. If I made more Movers, I'd either have to give them all the same sprite at the same time, or re-load the sprite each time - both bad choices. In a more complex tutorial, I'd use static texture variables. If you don't know what that means, google "static variables". Useful little things.

*"BUT I WANTED TO PLAY MUSIC!" - use an iPod for now. Sound has its own annoyances, and generally comes much much later - even after items like separate gamestates (sorta like GM rooms), dynamic object creation (like bullets), better input managers, and more

*"TL;DR" - well, its for people just getting into C#/XNA. So it has to be long.

From Vance - "Why are you doing X when Y is more efficient?" - because learning is gradual and if that weren't the case, I'd be spouting about separating your objects into quadtrees, sorting object depth via binary heap, etc.

If you have any specific questions, feel free to ask in my comments!

Comments

Vance_Kimiyoshi 14 years, 4 months ago

The entire point of XNA is to facilitate easy development across a number of Microsoft platforms (Windows, Xbox 360, Zune, and now Windows Phone 7).

Castypher 14 years, 4 months ago

Sorry to fall off-topic for a moment.

There's been a little mention of the DirectX SDK, which you suggested to me long ago along with C# and XNA. What does it do, exactly? Is it a devkit for 3D games?

DesertFox 14 years, 4 months ago

DirectX is if you want to get into the nitty-gritty stuff - its a step up from C# and XNA, because it is generally used with C++.

If you cannot handle C#/XNA, I strenuously say that you should not attempt C++/DirectX unless you are prepared for much frustration, confusion, and anger.

The directX SDK is about as low as you can go without writing your own 3D rendering code yourself. XNA is built off of DirectX. Although DirectX is an SDK, it is a very low level one.

Castypher 14 years, 4 months ago

Good to know. So the fact that I've ignored it doesn't hurt me. No really, that's good to hear. Do you sense my relief?

Juju 14 years, 4 months ago

*Facepalm* You guys… This is what happens we try something other than Game Maker.

sirxemic 14 years, 4 months ago

ATM the arguing is continued and hopefully will be resolved on IRC.

DesertFox 14 years, 4 months ago

This long-winded argument with blackhole was resolved in IRC.

It was awesome. Thousands died, and we rejoiced.

squeakyreaper 14 years, 4 months ago

Thanks to a year working in this language, working in GM is horribly difficult for me. Once you go to a "higher" language, it's hard to rethink your style.

Mordi 14 years, 4 months ago

Now that the argument is over you can focus on the next tutorial. Yay!

Infinity_Plus 14 years, 4 months ago

I read about half of it, but I have to get back to work, unfortunately. I'm definitely going to be picking up on this later though!