January 2009 - Posts

The Code Behind Paint Wars: Object Oriented Design

This post is the second in a series of posts about the code behind PaintWars. In this series, I will be talking about how the design and implementation of the game differed in C# and F#.  Along the way, I'll also be talking about some of the fun and exciting features of the F# language and examine how they can be used to solve practical software problems. For a description of the game, see this post.

 

For most readers, this post will not be as exciting as future posts about the design of Paint Wars will be, but every story has to start somewhere, and the story of Paint Wars begins with an object oriented design in C#.

 

The Structure of an XNA Game

This post is not meant to be an introduction to XNA programming, but I will include an extremely brief introduction to the XNA Game loop for our purposes.

As with most game loops, XNA games consist of two important methods: Update and Draw (in reality, there are a few more setup methods, but they don't matter yet). The XNA Framework calls the update function so that the game can progress from one state to another based on player input and whatever game logic is required. This usually happens many times per second. After the update function runs, the draw function is called to render the current game state to the screen. The XNA framework runs a game by continually calling these two functions until the end of the game occurs.

 

The Model Class

In Paint Wars, most of the update and draw logic is handled in the Model class, which is a container for all of the objects in the game. It's responsibilities include the following:

  • Telling all objects in the game to update and draw. Draw order matters, and the Model class handles this.
  • Performing collision detection on game objects. The Model class uses basic spatial partitioning to improve the performance of this. I'll go into more detail about this in a future post.
  • Providing access to the Canvas, which is the interface for determining the status of the paint on the game board. Again, more on this in the future.

 

Class Hierarchy

PaintWarsClassHierarchy

 

The above diagram shows the class hierarchy for the C# version of Paint Wars. The base class, WorldObject, has abstract methods for common operations such as drawing, updating, moving, and taking damage. WorldObject is considered to be a fat interface because the extra methods (fat) are not needed by some child classes.

This type of design has the advantage of being easy to consume.  When a player presses the nuke button, the Cursor class asks the Model for a list of nearby objects and calls the TakeDamage function on all of the objects. It doesn't matter if the object can actually be damaged. Objects that can be damaged will override the TakeDamage function.  Otherwise, the base class implementation will just "do the right thing".

Similarly, Update and Draw are easy operations for the Model- just iterate the list of WorldObjects, and call either the Update or the Draw function. Polymorphism and Object Oriented Programming 101, right?

 

Observations

Now that all the busy deign work is out of the way, lets make a few observations about the above OO design:

Pros:

  • You don't have to modify the rest of the code to add new types.
  • There is no "switch on type" code (if/else blocks or switch statements) that can introduce subtle bugs when new types of objects are added.
  • Separation of concerns and encapsulation means that it's easy work in teams (you write BlobObject, I'll write SpawnPoint). It also makes debugging, testing, and maintaining code, a lot less painful.
  • Cool diagrams.

Cons:

  • Complexity is handled through inheritance. If you want an object to behave differently, you create a subclass and override the appropriate method.  This works well on paper, but in code, it usually becomes difficult to follow.
  • There are a lot of classes to worry about. Best practices say that each class belongs in a different file. Without a fancy diagram, it is difficult to see the big picture. A lot of code is wasted on class definitions and plumbing.
  • Flexible to a point. Think of a thin wooden rod. It's very flexible if you only apply a little pressure, but if you put too much pressure on it, it snaps. Similarly, the above class hierarchy appears to be very flexible if you have small requirements changes, but if your requirements change drastically, you will be in a world of pain until you rewrite your plumbing.
  • A lot of mutable state hidden inside of classes.  Code designed this way will not work in a multithreaded environment without some serious modification.

 

In the next post, I'll describe the functional/F# design and examine the tradeoffs between the different designs.

Paint Wars on Display at CodeMash

Paint Wars Logo 

At the SRT Solutions booth at CodeMash this year, we will be showcasing Paint Wars, which is a Wii remote controlled game built on the XNA Framework.  The game was originally developed in C# during the Winter of 2007 by Sean Petty, Marshall Weir, and myself as our senior project at the University of Michigan.  During my learning time at SRT Solutions, I rewrote Paint Wars in F# as a learning exercise, and in the coming weeks, I will be blogging about the lessons that I learned while developing the F# and C# versions of the game.

UPDATE: Check out the YouTube video

Paint Wars The Game

 

 PaintWars1

Paint Wars is probably described best as a Real Time Strategy game, but it does not fit very well into most video game genres.  It is loosely based on the board game Risk.  A game is played by four people, each of which are assigned a color (Red, Blue, Green, or Yellow).

 

A group of yellow blobs

Players guide an army made up of blobs which are little creatures that move on their own and automatically paint the background with that player's color.  Blobs will move towards enemy blobs and attack them, and also prefer to move towards areas on the background that are of an opposing player's color. 

 

A green spawn point

All players also have a spawn point, which produces more blobs every few seconds.  The more of the background that is painted by a player, the more blobs that they get for each spawn. 

 

A red cursor picking up blobs

Players can pick blobs up with their cursor and move them around the map, and they can also drop a paint nuke on the canvas every few seconds.  Paint nukes will kill any blobs within a small radius of the cursor, and will also paint the background with the controlling player's color.

 

PaintWars2

Every few seconds, the total amount of each color on the background is calculated.  If the ratio of any one color to the rest of the colors is below a predefined limit, the player corresponding to that color is eliminated.  The last player standing wins the game.

 

That's it!  I'm looking forward to sharing more about the game and the code behind it in the coming weeks.