Friday, May 11, 2012

The Move to XNA

The Old Code

Let's get something straight right from the get go: I am not a great programmer.
I'd like to think I'm a pretty okay programmer, but I may not even be that. Please bear that in mind as we take a quick tour of my initial code for my Mega Man clone.

Let's start by listing the files and what's inside of them:
GameEngine.cs - Contains the main game loop and references to the Graphics and Physics management classes as well as a List of all the game objects that are in memory.
GameObject.cs - Contains basic logic for tiles in the game. Is it an object that can be moved through by the player? Is it affected by gravity? Where is it located? What's it's movement speed? Direction? Acceleration? All these questions are (or were intended to be) answered here.
AnimatedGameObject.cs - Not everything in our game will have static poses; some things need animation! This inherits GameObject and really only adds functionality for animations.
Player.cs - Inherits from AnimatedGameObject. Most of the code in this class deals with responses to user input, e.g. when the Jump button is pressed and we are on the ground, do a jump.
Bullets.cs - Handles bullet creation and pooling. Also contains basic logic for bullets.
GameControls.cs - This is a static class which contains a list of properties for different actions like move left and jump.
Physics.cs - This class contains my physics methods, such as they were. There are really only two: ApplyGravity(GameObject) changes the given object's acceleration given what direction gravity is affecting it (my thinking being that doing this here would let me have gravity from multiple directions later) and MovePhysicsObject(GameObject) which then applies the object's acceleration vector to their x,y position.
Graphics.cs - This class was supposed to handle all graphics calls that my other classes made as well as initializing the game window. The idea was that by funneling all the graphics calls through this class, if I ever needed to move the code to another platform then I would only have to re-write classes that touch the platform's APIs, like graphics.
GameWindow.cs - The final class in my list mostly just handled receiving user input and updating the GameControls class as needed. This is arguably not the place for this logic, but I never got far enough on this version of the code for it to be a problem.
So that's what the code structure looks like. The player and the level that I was testing in, seen below, were generated each time the program started just before entering the game loop (bad). I began the project thinking about how to implement the game in a clean and easy to read way, but ended up with a mess (double bad). It was nothing that couldn't be cleaned up in an hour or so of refactoring, but that's an hour of time not spent putting in something new. Reading user input was handled in the GameWindow class instead of the actual Control class (triple bad) and the code in the Physics class was barely functional (so bad).

It's hacky, poorly designed, and really only had one thing going for it: it worked. All the problems I listed above would have to be fixed before I felt good about releasing it, and the longer I left them unfixed the harder it would be to fix them, but there's something to be said for something that works properly. Mega Man was animated, he ran back and forth and when you pressed the right buttons he would jump and shoot. Had I continued with this code, the next step would be to fix my design problems, work out any bugs and generally get my program into shape. Ironically this is exactly what I was doing when I decided to abandon this.

Why I moved to XNA

My first experience with XNA was several years ago shortly after graduating college. I hacked together some tile generation system with some basic MS Paint sprites. Figuring out everything was pretty terrible since the documentation was spotty in places and my own knowledge wasn't enough to fill in the gaps. I gave up. More recently I was looking at different engines and frameworks for making games. I tried my hand at Unity, but I never felt like I was using it properly and since all the online resources about how to use Unity were either aimed at people who a) couldn't use a mouse or b) had several years of game design experience already, I was out of luck. I did bumble my way around it for a while, but eventually got frustrated with my lack of meaningful progress. I also tried my hand at GameMaker, which seemed promising at first. It was simple to get into and get going, but I quickly became frustrated by the lack of control over the actual code and my own lack of understanding of how the system worked. I had gone through a few other engines at this point and was, I suppose, just frustrated by spending all my time navigating GUIs and not doing any actual typing. So I started writing the code I outlined above.

My roommate had been using XNA for quite a while and had a book on XNA 4.0 handy. I flipped through it and was pleased with how easy things looked now. Things I wanted were already set up for me, only a minimum of coding for them was involved. I could target the Xbox 360 as well as my initial target of Windows (I'm not planning on putting a Mega Man clone on Windows Phone), there are contests I could enter, there are a ton of tutorials for XNA stuff now (no such luck for my windows form code), and there's even a platformer starter kit that shipped with XNA 3.1 to provide me examples. I could go on, but that's a pretty decent set of reasons right there.

I took the plunge and within two to three hours with my old code and the platformer starter kit as a reference, I had gotten back to my previous level of functionality and then some. One of the things I took from the starter kit was the Level class which, with a minimum of modification, will allow me to have a basic tool to generate levels from easily editable text files. Most of the code is still in there and the class will need to be majorly overhauled for my purposes, but what's there is a good start.

There are a few things about the starter kit that make me scratch my head. For example, take a look at this code from the Player.cs class of the starter kit:
// Calculate bounds within texture size.            
int width = (int)(idleAnimation.FrameWidth * 0.4); //12.8
int left = (idleAnimation.FrameWidth - width) / 2; //9.6
int height = (int)(idleAnimation.FrameWidth * 0.8);//25.6
int top = idleAnimation.FrameHeight - height;      //6.4
localBounds = new Rectangle(left, top, width, height);
This code is in Player.LoadContent(), which gets called once at initialization of the class. The selection I've pasted here calculates the localBounds object which gets used in a few places doing some very useful things like checking for collisions. The numbers in the comments at the end of each line represents what the calculation comes out to with my 32x32 px Mega Man sprite. I have no idea what the magic numbers above are for and the comments near the code block provide no insight on the matter either. There's a couple places around the starter kit where things like this happen, and it's more than a little infuriating. I assume that they made the starter kits so that people new to XNA could learn from them (which would explain why all the functions are otherwise immaculately commented), so it's unfortunate that there's no explaination for this. Perhaps there is and there's something I'm missing, in which case I'll edit the explaination into this post later. It certainly makes it more difficult to calculate numbers than it should be though. I thought I could pass the player's Position variable for where the player bullets spawn, and imagine my surprise when the bullets fired from his feet!

Overall the starter kit gives me a good idea of some things I need to look at to get a respectable platformer going as well as examples of how to do some basic things in XNA as well as how the XNA devs thought the flow of the program should work. I recommend checking it out if you are unsure of where to start. You can download a version of the starter kit updated for XNA 4.0 here and read more about what Microsoft has to say about their kit here.

Screenshots

To wrap up, here's a screenshot of the old windows forms code, showcasing a particularly unfortunate problem with my detection of whether the player was standing on a solid surface:

Here's some screenshots of the new XNA code that I'll be optimizing and adding to the rest of today and tomorrow:
As you can see I am checking for solid surfaces under me better now.

Although apparently shots are coming out of Mega Man's head. Oh well, at least they're shooting, that's good enough for now.

Jumping and shooting works too! Look!


No comments:

Post a Comment