More Screenshots!

September 26, 2009

I’ve been taking a lot of them lately:





Understanding the Content Pipeline, Part 3

September 26, 2009

Content Processors

In the last post, I talked about Content Imports and their role in opening an asset file and parsing its content.  Ultimately, the importer produced a managed object that represents the data in the file.  This managed object is then passed onto the Content Processor for further work.

Content Processors follow a similar design pattern to importers.  You write a class that derives from ContentProcessor<TInput, TOutput>.  TInput is the type of the managed object that the importer produced.  This will become the input to the Process() method that you will override.  The TOutput type is the return type that the processor will produce.  Many times, the input and output types will be identical (however the output will have been transformed in some manner as performed in the Process() method).

In my game, I wrote a processor that builds a direct acyclic word graph from a list of strings each representing a distinct word in a dictionary.  The process of building a word graph is a bit complicated and I’m not really going to go into it at this point (this is about the content pipeline after all).

The idea that the content processor processes content is very wide open.  You could write many processors that did as much pre-processing as possible of game assets.  The number of possibilities is limitless.

Here’s an example of a basic skeleton of the content processor I wrote:

[ContentProcessor(DisplayName = "Word Graph Content Processor")]
    public class WordGraphContentProcessor :
                   ContentProcessor<WordListContent, WordGraphContent>
         public override WordGraphContent
              Process(WordListContent input, ContentProcessorContext context)
               WordGraphContent output = new WordGraphContent();
               // do the processing...
               return output;

As you can see, you override the Process() method which takes the input type as a parameter.  You then perform whatever processing you need to do on your asset.  In the end, you return the transformed object either as a new type (as is in my case) or as the same type as the input type.

You must attach the ContentProcessorAttribute to the class so that XNA Game Studio will recognize the processor.  You can give it a pretty DisplayName that will appear in the properties window of Visual Studio.  When you open an asset and attach to your content project, you can select the custom processor from the drop down list by this friendly name you give to it.

Now is probably a good time to discuss something important.  If you are making an XBox 360 game, it is important to know that niether your ContentImporter or ContentProcessor that you custom build can ship with your game.  This is because the dependency assembly (Microsoft.Xna.Framework.Content.Pipeline.dll) is available on Windows only.  This makes sense because building your game and its assets only ever occurs on the development machine which is running Windows.  This is important to note because it means that your importers and processors must be contained in a separate class library project within your game solution.  You need to reference the content pipeline assembly to get access to the ContentImporter and ContentProcessor types.

This is another “gotcha” that I should explain.  If your assembly with the importer and processor references any XNA Framework code (like Texture2D for example), it must then reference the Windows version of the XNA Framework.  If you are writing a Windows-only game, this isn’t a problem, but if you are writing an XBox game, there is a separate framework version for the 360 and you cannot use it in this project.  Furthermore, your Content sub-project of your game must reference your custom assembly containing the importer and processor.  If it doesn’t, the content build system will not know about your custom importer or processor and will not present them as a choice to you to use.

I think this pretty much wraps up this very basic introduction to the content pipeline.  Read the XNA documentation to get more info and take the next step in your understanding.  Once you realize how powerful this build process is, you will start thinking of new and inventive ways of using content.

Understanding the Content Pipeline, Part 2

September 21, 2009

Content Importers

In the last discussion, I explained how the content pipeline works from a high level perspective.  In this post, I will explain what content importers are and how to write them.

Recall from the last post that content importers are pieces of code that open a file and parse its contents with the ultimate goal of transforming that file into a managed object.  This object is then passed along further down the pipe to the Content Processor.

In my game, I needed to write an importer because I have text files containing words that need to be transformed by a custom processor.  The XNA Framework doesn’t come with a “text file” importer that fits my needs (which is not surprising because this is such a specialized task).

To write such an importer, you begin by writing a class that derives from ContentImporter<T> (I called mine WordGraphContentImporter).   The generic parameter T in this case is the type of the managed object you are ultimately creating.  In this case, my managed object is a simple object called WordListContent.  This object is incredibly simply and merely wraps a List<string> which is a flat list of all of the words read out of the file.

In order for XNA Game Studio to detect this importer, you must also mark your importer class with the ContentImporterAttribute.  You can specify DisplayName property to give it a nice name in the UI and a DefaultProcessor so that when users choose your importer, it defaults to a related ContentProcessor as a default and a shortcut.

To implement the actual importer code, you override the Import() method.  This method takes a string file name representing the file you are going to open and a ContentImporterContext for advanced use.  It returns a type T (in this case WordListContent).  Here’s a class skeleton:

[ContentImporter(DisplayName = "Word List Importer",
DefaultProcessor = "WordGraphContentProcessor")]
public class WordGraphContentImporter : 
      public override WordListContent Import(string filename, ContentImporterContext context)
         return new WordListContent();

In my Import() method, I basically create a new StreamReader to read the file using the given file name.  I then read each line and do a couple of checks:

  1. I make sure that the line read actually has data on it and isn’t just whitespace or empty.
  2. I check that the word has not been already used.  I use a dictionary to check for redundancy.
  3. I also make sure that the word is cased correctly and that it is less than or equal to the maximum word length.  My game arbitrarily limits words to 15 letters and I filter out any longer ones here in my importer.

If the word passes all of these tests, I place it on the list contained within my WordListContent object.  When all line reading is complete, I close the file and return the managed WordListContent.

When do you write importers?

You should only need to write an importer if you are dealing with a file format for which no importer can already be obtained.  The XNA Framework and other third party sources have written many file importers that cover many file formats.  Here are some that the XNA Framework provides:

  • XmlImporter – reads .xml files in a specific schema format.  If you have any data that could be marked up as XML, you should use this very fast and robust importer.
  • FontDescriptionImporter – reads .spritefont files which is really just a special case of the XML importer.
  • FbxImporter – reads Autodesk .fbx model format, version 2006.11.
  • EffectImporter – reads Effect .fx files, shaders written in HLSL.
  • TextureImporter — reads all standard graphics file formats such as .png, .bmp, .jpg, .tga and others.
  • XImporter — reads native DirectX model .x format.

It is also quite possible that somebody else besides Microsoft has written an importer that might cover your needs.  For example, a lot of artists use Photoshop and there is an importer that can support Photoshop .psd format.  This could be used to keep your artist happy so they don’t have to save as .png all the time and keep Photoshop specific content without making two copies of the file.

Next time, we’ll discuss ContentProcessors which you are more likely to write than importers…

Understanding the Content Pipeline, Part 1

September 18, 2009

Of all of the great things that XNA Game Studio and Creator’s Club provides, none of them in my opinion is as interesting and as underrated as the Content Pipeline build process.  This is a very cool feature that builds your raw game assets into compact and formatted binary representations that are quicker to load at runtime.

Not only is this my favorite feature, it is perhaps something I struggled with for quite a while.  I know people that hate the content pipeline and use it only for basic texture and model loading.  At first glance it is very complicated and hard to understand.  I didn’t go much beyond the basics at first, but now I tend to think about how I can use the content build system to tackle a problem before anything else.

I think learning by example is always a good idea so I will give an example of how I use the content pipeline.  Hopefully if you are struggling with it as I did, this example will help.

My example is focused around a big problem my game has to solve.  Since it is a word-based game, it needs a list of words that define its dictionary and this list needs to be fast to load, fast to verify a word as correct, and fast to search for the AI to make use of.  My problem statement is this:  I want to take an arbitrary list of N words and build a data structure that will make it possible to fulfill the requirements as stated above.

After some searching around, it was decided that I should build a directed acyclic word graph for word lookup and searching.  This data structure is the runtime piece that we use in the AI and during the game to validate the correctness of words.

Since the word list can be quite long, the word graph can take some time to load.  A question arises: do we really want to load this word list into the fast word graph at runtime when the game or level loads?  Well, we can try it out.  After getting a hold of my 170,000 or so word list, I tried to load it in my game.  Result: not so good.  Over 1 minute was required to load and transform these words.  Now my algorithm is probably less then optimal, but another question arises from that perspective:  do I want to spend the time to try to make this faster or just pre-compute the word graph during build?  I thought about it for a while and concluded that attempting to optimize the word graph build algorithm was pointless.  Pre-computation won out.

Since I’m going to be pre-computing, the content pipeline build process will be how I will do it.  My goal will be to drop in a text file containing all my words and transform this file into a binary representation of the word graph.  This binary file will then be loaded directly from the ContentManager in-game and should already be transformed and should have a small footprint on disk.  It shouldn’t take any longer to load the word graph than a large texture or sprite font.

So let’s begin with a simple review of how to build a custom processor that will build my word graph via the XNA Game Studio Content Pipeline…

There are 3 parts to the build process of a game asset:

  1. Content Importing – this is the process of opening a game asset file (in my case, a text file) and transforming it into a managed object.  Importers are all about understanding file formats and getting file contents into managed objects so that the data may be used farther down the pipe.
  2. Content Processing –  this is the process of massaging and transforming the managed object obtained from importing.  Processing is a very wide-open aspect of the pipeline.  Almost anything could be done here.  For my example, I will be running my word list through my own custom WordGraphBuilder which is an object that does the word graph build algorithm.
  3. Content Writing – this is the process of taking the final processed object from step #2 and serializing it to binary.  In XNA Game Studio 3.1, this process is automatic and the system can generate this step for you.  However, in my code I wrote it before 3.1 was released and I’m doing some fancier logic to conditionally write certain aspects of the object to the binary stream.  I do think it could be rewritten to take advantage of the automatic serialization capabilities in 3.1, but so far I have not explored that avenue out of time constraints (in other words, if ain’t broke, don’t fix it!).

When you place a content file in your Content project in XNA Game Studio, you have the option to specify the Importer, and Processor that will be used to build your asset file.  Many importers and processors ship with the XNA framework, but you can write your own.  Obviously for example, I needed to write my own because there isn’t a text file importer suitable for my needs and there certainly isn’t a processor that builds a word graph that ships with the XNA Framework!

In the next part of the series, I will start going over the individual three parts in detail.  Stay tuned…

New Screenshot

September 18, 2009

It’s been awhile since I last updated the screenshots.  There’s a new one now of the fire effect at the top of the game area!  Check it out on the screenshots page!

This screenshot also shows the AI in action.  Still shots just don’t show how good it is shaping up to be.  The fire effect is animated and was made using a custom fire particle effect made with the Mercury Particle Engine.