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 1Install 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 2Open 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 3Run 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
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
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:
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
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:
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 BREAKOkay, back. Time for
Step 4Now 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…
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!
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.
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!
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:
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 5First, 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!
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.
Hey! Error! Because it needs arguments! Specifically, its starting X and starting Y! Change it to this:
player = new Mover(200,200);
and in Update/Draw, add respectively:
Now, back to LoadContent - after you create the player mover, add this:
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!
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.
public void Draw(SpriteBatch spriteBatch)…
And pass it in Main Draw
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!
Or, play Plants Vs. Zombies.
nice I've tried XNA before and I'm going to set back a an hour or so of my time to read this :D thanks a lot!
@DF: Tools. Self-made.
Image editor.Room editor.Maybe even an object property editor.Other plugins to make life easier.Tools.Yeah.I went through it. Nice tutorial, even though I didn't learn much new. I've tried to learn XNA, and gotten to a point where I just didn't want to learn more.
Now I want to learn more again.I wish someone would do a "what's the XNA equivalent of GM's rooms, objects, sprites, scripts, etc". I know most of these things, but gamestates and handling objects keeps confusing me. I always return to GM after a while of struggling with these things.You're in a perfect position to do that though, DF.I would also have liked to see a gamestates tutorial. Personally I don't understand how to do, for example, going from a menu into a room and then initialize it and then going from that to just the regular old updating and drawing.@Mordi - some good ideas. I think next blogtutorial will be include a bit of talk about major differences between XNA and GM.
I can actually answer two of those questions right away - the Texture2D class combines with SpriteBatch is the equivalent of GM's sprites, and there is no need for 'scripts' since it's all code anyway - you can create a "static" class specifically for random tidbits of code.Also, I already have a system that is equivalent to GM's rooms. I'll have to post it and give you guys a run-through.I'm sorry, I simply do not understand you DesertFox. I am currently not commenting on your tutorial here - obviously its just designed to teach a game, I never questioned that. However, you say this:
First off, to use an edited old saying: "a giant-ass audio system does not a game make". In other words, I'd just love to see someone make a fun game that everyone likes, simply out of an audio engine - no sprites or graphics. No doubt it can be done, but it would take a special creativity to succeed beyond more than a simple gadget trick.
I recognize that we are viewing this from different perspectives, so bear with me as I explain. A lot of people can work on and design their own sprite animation framework. They will each do it different ways - the way that is more efficient for them, better for their game. Forcing them to use a specific animation framework that is probably not going to be specific to their needs will in the end cause many more problems than it solves. I have seen many, many different sprite animation setups, and they are in no form compatible without heavy modification - modification that would inevitably take more effort than to create your own animation framework.Audio is different - not many people have more than the basic understanding of audio manipulation. Sound is an important, yet complex-if-you-want-to-do-more-than-just-play-a-sound thing. Its sort of why sound-engineering is its own separate job - it requires a different sort of knowledge and experience. A single, robust audio system is good in this case, because it fills in the gaps that most developers can't, as well as the fact that most developers will only ever need the same, basic sound manipulation, doubly so with XNA.Anyhow, I'm not to well versed on audio, but as far as I can tell, XNA just gives you similar dynamic audio access levels as other things, via DynamicSoundEffectInstance. XNA's audio-engine isn't as big as you think, you can apply distorts, panning, pitch alteration, or get buffer access*. Hell - you can do similar things in flash!*Buffer access being important because you can do anything if you have the knowledge.The only other thing I can imagine is if you actually mean a sound-editing tool in the development environment, which A) I can't seem to find and B) would be a tool to generate resources, and have nothing to do with programming.Being able to edit audio on-the-fly is akin to being able to edit a texture on-the-fly, which you can very much do. So no, XNA's audio engine (which is a different sort of engine than a game engine) is not the equivalent of an animation framework.To end, animation is not resource management. Its resource use. Utterly, completely different things.A final note, a pre-built resource management kit is not at all like having a pre-built car. It's not even the car kit. Its part of the machine shop - something to bend/form the metal (resources) into shapes that you can use. An audio engine would be part of the car kit - like a sound-system.The point on the animation systems being radically different is likely why no animation system is actually implemented, but this whole "animation isn't audio" thing is debatable. XNA's audio engine is huge, so big that I wrote my own audio engine called TinyOAL because I couldn't get XNA's audio engine to just play a stupid sound file. This was a few years ago so it has probably since been rectified, but it was still a hilarious example of how feature bloat is a serious problem. Likewise, you seem to be making arbitrary distinctions here - even though XNA's audio engine is a prebuilt part of an engine, it doesn't matter because most graphics people don't know how to work with audio.
So what you are really saying is that it is unreasonable for a graphics engine to have an animation system because its too dependent on the game itself, not this extremely arbitrary and nonsensical distinction between an SDK and an Engine that XNA itself blurs.I will say that is a valid point. However, there is no reason you couldn't make an animation framework that is able to implement animations that a lot of games share, like, for example, sprite animation. I built a little sprite helper class, which just constructed a few animations and played them. It is very simple, and there is no reason it wouldn't be useful to have in XNA for everyone making sprite based games. Isn't the entire point of XNA to make game development faster? Doesn't this lend itself to a sprite helper object that would be very easy to code provided you knew what you were doing?