Geeks With Blogs
Code.Blog Code Talk for the Game Developer

I have been hard at work recently porting Pong RPG from a PC-only XNA game to an Xbox 360 game in the hopes of potentially releasing it on the Xbox Indie Games Marketplace.  Pong RPG (PC) was a great success in my mind because even though it was flawed, it was a full game product that I created from scratch (almost all by myself) since my days in High School (which was when I first created a full game).  While it was great to finish the game, the code was extremely difficult to read and very static.  I learned a lot from finishing that game and have been applying the recently learned techniques to every project since then.  So now that I'm porting the game to Xbox, what exactly goes into porting bad code?

I have been fully re-designing all the code from scratch, essentially rewriting the whole game.  The nice thing though is all the time I spent painfully positioning each sprite on the screen is already done since I'm using the old code as a reference (all my screen positions, rotations, scalings, etc... are there for me).  At home I currently have a 32" plasma HDTV.  When I first created Pong, I built the entire game using a square window and now that I'm in HD, I need to update my old artwork that was built for that screen size (for example, a 1024x768 piece of art with a 1.0 scaling factor won't fill a 1280x720.  Thankfully most of the artwork is finished and only requires updates to shape it correctly and to swap out PC-specific art with Xbox-specific art.

How has the code been cleaned up?  Almost all of the Sprite loading, storing, and rendering is handled by generic classes allowing me to re-use them in future projects.  Here's the setup (class names may differ from my real code):  there is a ContentProcessor class that parses '.LFI' files (simple .TXT files with a different extension standing for 'Load FIle').  Each line of the Load File defines either a SpriteSheet or a Sprite.  A SpriteSheet is a single PNG file containing one or multiple pieces of artwork within.  In Pong RPG (PC) every single sprite had it's own file and required a Content.Load<Texture2D>() call.  The SpriteSheet load file commands simply specify a load file to process. 

The Content Processor then takes the PNG file defined by the Sprite Sheet command and loads it into a TexturePool.  A TexturePool is a class that inherits from List<Asset>.  Asset is a custom struct that only holds two properties about a specific game asset: the Texture (Texture2D), and the Name (String).  Here is the definition for the Load() function of the ContentProcessor class:

    public void ContentProcessor.Load(String fileToLoad, ref TexturePool textures, ref SpritePool sprites)

Obviously the first defines which LoadFile to load.  The second parameter, textures, defines where to store the Assets loaded by the ContentProcessor.  We'll talk about the SpritePool stuff in a little while.  As a side note, ContentProcessor is defined as a static class so we pass the TexturePool and SpritePool variables by reference to avoid requiring the programmer to create an instance of this class.

So ContentProcessor begins parsing the Load File and hits a SpriteSheet command (which should always be first since it's impossible to load a sprite without a related PNG file).  Let's assume we're loading MainMenu.lfi (the Load File for the main menu) and there are 2 PNG's we need to load.  We would have 2 load commands similar to the following:

    SpriteSheet, Sprites\MainMenu\MenuBG
    SpriteSheet, Sprites\MainMenu\MenuItems

In our case, MenuBG is a PNG with artwork that takes up the entire file, and MenuItems is a collection of pieces of art placed around in the file.  After ContentProcessor parses these lines, our TexturePool variable should be populated with 2 items named MenuBG, and MenuItems (the name defaults to the filename). 

Unfortunately that won't be entirely helpful since that only defines the textures and not which parts of them to draw.  This is where Sprites come in and are extremely handy.  After the first SpriteSheet command to load the MenuBG PNG file, we will call a Sprite command to tell the processor to load a sprite and place it in the supplied SpritePool.  SpritePool is similar to TexturePool in that it inherits from the List class, however SpritePool inherits from List<Sprite>.  Sprite is a custom class that holds information about the positioning, rotation, scale, boundingbox, and the source rectangle we use to pull from the SpriteSheets previously loaded.  Here's an example of a Sprite ContentProcessor command:

    Sprite, MenuBG, M-BG, CENTER, CENTER, 0, 1.0, 1.0, 0.1, true, 0, 0, WIDTH, HEIGHT

This looks very cryptic but is really easy to understand.  All it does is define what the ContentProcessor should load, how it should load, and where it should place it.  Here is the definition of this command:

    Type, SpriteSheet Name, Sprite Name, Screen X-Position, Screen Y-Position, Rotation, ScaleX, ScaleY, Layer, Visible, SourceX, SourceY, Source Width, Source Height

The only commands there that may throw you off are the last four containing Source ____.  When you create a new Rectangle in XNA it requires 4 parameters: the left side X-coord, the top Y-coord, the width, and the height.  Essentially, the first two parameters define the upper left corner of the Rectangle.  In our load command, things like CENTER, CENTER, WIDTH, HEIGHT, etc... are simply built in commands that define certain things about how to load it.

We will see more of this in a future code example with a more detailed tutorial!

Posted on Friday, November 6, 2009 5:31 AM Tutorials | Back to top

Comments on this post: Development Diary: Pong RPG

No comments posted yet.
Your comment:
 (will show your gravatar)

Copyright © Matthew Christian | Powered by: