Popular XNA myth #623: "XNA forces me to use the Content Pipeline any time I want to load data into my game".
- Add data files (binary, XML, text, or whatever else you prefer) directly to your main game project
- Don't add them to your content project (that is specifically for building data with the Content Pipeline)
- Set their Build Action property to Content, and Copy to Output Directory to Copy if newer
- In your game code, call TitleContainer.OpenStream, and pass the resulting stream to whatever I/O API you prefer to use
But of course, 'possible' and 'easy' are not the same thing. If you choose not to use our built-in Content Pipeline, you should expect to have to do some work to replace it. Depending on the format of the data you are trying to read, this could range anywhere from trivial to very hard indeed.
It is important to understand that the Content Pipeline has several layers. At the core is an infrastructure which provides generic services for building and loading any type of content:
- A standardized way to package arbitrary content importing and processing code behind consistent interfaces
- An MSBuild task that uses these components to build content, tracking dependencies for incremental rebuilds
- Visual Studio user interface for selecting source assets and setting parameters that control how they are built
- An extensible mechanism for serializing arbitrary .NET types into a compact binary format
- An efficient runtime loader for rehydrating these binary files and tracking object lifespans
- Generic mechanisms for handling cross references and dependencies between assets
- A helper for serializing .NET types to and from XML
All that stuff is just regular C# code. If you wanted, you could write your own version from scratch that would do all the same things in the same way. It'd take you a while, but it would be possible.
Layered on top of the generic infrastructure, we provide built-in support for the most important types of content:
- Importers for common graphics and audio file formats
- A build time .NET object model for representing the data returned by these importers
- Processors for turning this object model into whatever format the runtime XNA Framework wants the data to be
- Assorted helpers for common data manipulation operations, which make it easier to write your own processors
Again there is no magic here. You could reimplement all this yourself, given sufficient time, skill, and determination.
So what CAN'T you do yourself?
It all boils down to what runtime type you want to load into. It's no use being able to read whatever you like from whatever files you like, if you then have no way to set the data into its final resting place:
- If you are loading into a type that you created yourself, there is obviously no problem
- If you are loading into one of the XNA Framework types that have byte constructor parameters or SetData methods (eg. Texture2D, VertexBuffer, SoundEffect), again there is no problem: just create the object and set your data into it
- Some XNA Framework types (Texture2D, SoundEffect) provide a FromStream method that can make this easier (but be warned: FromStream is usually slower than loading via the Content Pipeline)
- There are four XNA Framework types that have no public constructors or SetData methods, and thus no way to get data into them other than through the Content Pipeline. For these types, you must either use the Content Pipeline, or not use the type at all:
Replacing the built-in Model class is actually a common, sensible, and easy thing to do. Replacing SpriteFont is more work, but still possible. Replacing Song and Video is not really feasible, so you do indeed have to use the Content Pipeline to build music and video data.
Note: this article refers to XNA Game Studio 4.0. Some details varied in previous versions, but the principle remains the same.