Wednesday, May 30, 2007

"How to Keep Your Code From Destroying You"...or not

Slashdot's carrying a link to an article called How to Keep Your Code From Destroying You by Jeff Vogel, the summary of which is:

  1. Comment code
  2. Use constants
  3. Use descriptive variable names
  4. Include error handling
  5. Don't optimise until you've found you need to
  6. Favour clearness over cleverness
Tips which are valuable to developers just starting out more than anything. Any developer who's been even slightly mentored will know these instinctively anyway.

But I'm afraid I really have to disagree with the remark about comments. Some circumstances will of course require the odd comment around it, but if a chunk of code requires a comment at such an in depth level, then the code needs to be refactored.

Take this example of Jeff's:
// This procedure moves the bullet upwards.
// It's called NUM_BULLET_MOVES_PER_SECOND
// times per second. It returns TRUE if the
// bullet is to be erased (because it hit a
// target or the top of the screen) and FALSE
// otherwise.
Boolean player_bullet::move_it()
{
Boolean is_destroyed = FALSE;

// Calculate the bullet's new position.
[Small chunk of code.]

// See if an enemy is in the new position.
// If so, call enemy destruction call and
// set is_destroyed to TRUE
[small chunk of code]

// See if bullet hits top of screen.
// If so, set is_destroyed to TRUE
[Small chunk of code.]

// Change bullet's position.
[Small chunk of code.]

Return is_destroyed;
}
Some points on this:
  • Those "small chunks of code" preceded by the comment are clear candidates for being moved into their own methods
  • A description of a method is fine, but why comment on where it is used? I doubt this comment will be updated when the method is used elsewhere
  • If a method requires a lengthy comment like this it's named incorrectly or is doing too much. Call the method MoveUp() if it's moving the bullet up the screen.
At Esendex we rarely find the need to comment at such a detailed degree. Comments just aren't updated when new functionality is added for one, and if code can be self-documenting, then so be it. It may only take a minute to add a comment, but if you've already written the code that says what it does, then it's a minute saved.

If you've got a method called "CreateAndPersistCustomer" that returns a Customer object, then it's pretty obvious that the method will instantiate a Customer object, persist it, and return the object it's just persisted. The only worthwhile comment I can see that could be added to the top of this method is possibly a note on where it is being persisted, but this itself becomes redundant if your system only persists to one place.

I take exception to comments such as "This method will create a customer and then insert it into the Customer table. It returns the customer that has been inserted". It's pointless if your naming conventions are any decent.

There's feedback in the Slashdot article that points to this code file as an example of when code really needs a comment. But I have to disagree here for the most part as well.

It's fine putting comments around bitwise operations such as shifts and the like, but when you've got a comment on a function that says "Updates that pad's states according to event inputs", then surely you're just masking unnecessary code complexity with comments. Why not call the function UpdatePadState?

It's sort of a tradition in C to have obscure type names, but don't compound that fact by needlessly adding your own. Code with a variable called dInt won't compile or run any faster than if you call the same thing directionIndicator.

Have meaningful names for everything. If you want to create a class (or unit, or whatever your programming language calls an encapsulation of something) that models a game pad, for god's sake call it GamePad. If that object needs a property to get the last event then call it LastEvent. Anything else is just being lazy.

Self-documenting code is clean and easy to maintain. My personal view is that if someone has to rely on reading comments, and can't read the code itself then they have no business updating it. If you don't know the difference between i++ and ++i, then get back to school and don't come back until you're qualified.

Comments will always have their place, but don't use them when refactoring your code will make more sense. Comments won't stop you duplicating code, refactoring will. Commenting a chunk of code doesn't make it usable from another method--moving that code into its own method will.

So the next time you're about to write a comment just think to yourself (or say aloud to your partner if you're pair programming) "Why do I need a comment here?".

Think of ways in which you can name your methods, variable names and classes so you negate the need for lengthy comments. Save your comments for the complicated bitwise operations, complex predicate logic and the like--places where you really do need to comment.

Don't comment the top of methods when you could just name the method differently and make more sense to everyone.

Monday, May 28, 2007

Introduction to 3D in XNA

For my next XNA project I think I should move into 3D. The Spacewars starter kit still looks complicated but I think I've come up with a game idea that can make use of 3D while still being relatively simple to visualise.

I'm not certain of this idea so won't go into detail yet, but I will need to figure out how to place models in the world and move the camera for any 3D game to work.

I've found an introduction to 3D models in XNA, and an introduction to matrices that should help somewhat. I've managed to render one of the Spacewars models into the game and can move the camera around in the way that I want.

Now I need to get the model moving based on mouse input. Oh, and get some 3D models to display in the actual game. I've found Turbo Squid, but what I've found on there can't be imported into XNA and need to be opened in 3D modelling software, which I don't have.

Does anyone have any recommendations as to some free software I can use that will let me create my own 3D models?

Thursday, May 24, 2007

First Stab at Pong in XNA

Pong was probably not a great choice as an ongoing XNA project after all. I know what I said about the possibility of adding different features, but Pong doesn't really offer itself up as an interesting game that is going to be able to grab my attention for long enough to make something more of it.

So for now I'm done with it I think. It's playable, and relatively challenging. It keeps score and certainly looks quite retro in its black and white styling, but doesn't really do much else right now.

UPDATE: Changed the download location to something I have control over :)

For the time being you can download the code for the game if you want. Please read the readme.txt file as it explains a few things.

And please excuse my lack of knowledge when it comes to free file hosting, I just wanted something quick so I could upload the zip file. I might put the file somewhere else in the future.

I know that Ziggy wanted to download my first card game too, and I'll try to get that uploaded over the weekend.

But for my next project I think I'll try something new, no more copying existing games.

Wednesday, May 23, 2007

Documenting Enum Values

If you maintain a codebase that is of any decent size, chances are you'll have defined a fair few enum types. You may also need these types and their values to be documented somewhere, as not everyone who needs them could have access to the code where they are defined.

You could of course do this manually, but if you already have lots of types this could be tedious. Luckily its quite simple to write something that will do this for you.

The code below takes a parameter for the assembly path and name, and writes the values to the Console.



private static void DumpAssemblyEnums(string assemblyPath)
{
StringBuilder builder = new StringBuilder();

// flag to check if we should put the string we
// create into the Console output
bool hasEntriesToShow = false;
try
{
builder.AppendLine(string.Format("===== {0} =====", assemblyPath));

// load assembly from the path
Assembly assembly = Assembly.LoadFrom(assemblyPath);

// get the types from this assembly
Type[] assemblyTypes = assembly.GetTypes();

for (int i = 0; i < assemblyTypes.Length; i++)
{
try
{
// check if the type is an enum
if (assemblyTypes[i].IsEnum)
{
hasEntriesToShow = true;

// output the full type name of this enum
builder.AppendLine(
string.Format("==== {0} ====",
assemblyTypes[i].FullName));

// get the values for the enum type
Array array = Enum.GetValues(assemblyTypes[i]);

// loop through the array
for (int j = 0; j < array.Length; j++)
{
// output the numeric value by converting to a
// long
// use long as enum values can be larger than
// an int
builder.AppendLine(
" " + Convert.ToInt64(array.GetValue(j)) +
" = " + array.GetValue(j).ToString());
}
builder.AppendLine();
}
}
catch (Exception ex)
{
// output any error
builder.AppendLine("**ERROR: " + ex.Message + "**");
}
}
}
catch (Exception ex)
{
// output any error
builder.AppendLine("**ERROR: " + ex.Message + "**");
}

// push to Console stream if we've got something to show
if (hasEntriesToShow)
Console.Write(builder.ToString());
}



You can call this directly, or use a directory traversal to output all enum values in all assemblies in a directory.

This example will output into a format which can be used on a Wiki page, but you can edit it to output in any format you like.

Posting Code on blogger.com

Wish I'd known how to post code on this blog before. I've manually spaced code out before, and it was so tedious it took multiple tries as I just go so bored with it.

Thanks Neil