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:
- I make sure that the line read actually has data on it and isn’t just whitespace or empty.
- I check that the word has not been already used. I use a dictionary to check for redundancy.
- 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…