XNA provides a ContentManager class that is used to load and unload content. However, we are going to create our own that caches loaded content so we only have to load an asset once to use it repeatedly, and also supports unloaded specific peaces of content (when entering a different area, for example). Add a new c-sharp code file to your project, and create the class below:
public class IEContentManager : ContentManager
{
}
The first thing we’ll need is a list of all the loaded content:
// Cache of all the loaded content Dictionary<string, object> loaded = new Dictionary<string, object>(); List<IDisposable> disposableAssets = new List<IDisposable>();
Next we need to create two constructors that match those of the original ContentManager class. We won’t add any extra code here.
public IEContentManager(IServiceProvider services)
: base(services)
{
}
public IEContentManager(IServiceProvider services, string rootDirectory)
: base(services, rootDirectory)
{
}
We can now add the Load() function, which reads a single asset from file and stores it, or returns the cached version if there is one.
// Load an asset and cache it
public override T Load<T>(string assetName)
{
// Return the stored instance if there is one
if (loaded.ContainsKey(assetName))
return (T)loaded[assetName];
// If there isn't, load a new one
T read = base.ReadAsset<T>(assetName, RecordDisposableAsset);
loaded.Add(assetName, read);
return read;
}
void RecordDisposableAsset(IDisposable disposable)
{
disposableAssets.Add(disposable);
}
As this ContentManager is caching content, we also want to add a way to ask for a piece of content and be guanteed a fresh copy, which can be modified or worked with freely without worrying about other components touching it. We will call this function LoadFreshCopy():
// Load an asset and be guaranteed a clean copy of the object.
// Note that if this function is used this copy of the asset cannot
// be individually unloaded
public T LoadFreshCopy<T>(string assetName)
{
return base.ReadAsset<T>(assetName, null);
}
Next we will add a function that unloads a specific piece of content (assuming it was loaded with the regular Load() function):
// Unload a single asset
public void UnloadAsset(string name)
{
if (loaded.ContainsKey(name))
{
if (loaded[name] is IDisposable && disposableAssets.Contains((IDisposable)loaded[name]))
{
IDisposable disp = (IDisposable)loaded[name];
disposableAssets.Remove(disp);
disp.Dispose();
}
loaded.Remove(name);
}
}
Finally, we will add a function that unloads all content:
// Unload everything
public override void Unload()
{
foreach (IDisposable disposable in disposableAssets)
disposable.Dispose();
loaded.Clear();
disposableAssets.Clear();
}
And that’s it! Now we just need to update the rest of the engine class to use our new ContentManager. Simply replace all the references to the ContentManager class with references to the IEContentManager class. In the engine class, the following three lines of code:
ContentManager content = null;
public ContentManager Content { get { return content; } }
and
content = new ContentManager(Services);
become:
IEContentManager content = null;
public IEContentManager Content { get { return content; } }
and
content = new IEContentManager(Services);
And we’re done! We now have a ContentManager class that will be more useful to us when we start making complex games that require more direct control over content.
Note: much of this post is inspired by Shawn Hargreaves’ post “ContentManager.ReadAsset” (http://blogs.msdn.com/shawnhar/archive/2007/03/09/contentmanager-readasset.aspx).
Download the Code for this Chapter
« Game Engine Tutorial Part III, Chapter 4 – Engine [Updated 11/5/2009 11:12PM PST] Game Engine Tutorial Part III, Chapter 6 – Loading Content »

The XNA 3.x ContentManager shares loaded assets already as far as I know. I remember I read this in the creators forum somewhere.
Yes XNA already does this. Though there may be utility for this with much larger projects where you don’t want to load everything upfront. I’m not sure becuase I don’t know how XNA caches stuff relative to memory availablity.
XNA allows you to load all content upfront and then any subsequent loads of a given texture during runtime will simply be returning a reference to the already loaded content.
This is why many developers pre-load everything upfront. It makes runtime loads instantaneous since it’s just a reference return if it was loaded previously in the contentmanager.
Here is a brief Cretor’s Club Forum post that spells it out:
http://poserworldsubscriptions.com/Clothes/baboots.asp
Allan
This is another one of those cases where, even though XNA already does something, I’m either re-implementing it or expanding on it for the sake of demonstration. XNA has it’s own component system, but if I just used that then we wouldn’t learn how to write that kind of code later on when we’re not working with XNA.
This is just a simple demonstration of a caching system, which is used all the time elsewhere in code, and it also slightly expands on the content manager by allowing the unloading of specific content which may be useful for larger games.
Hi,
The UnloadAsset() method will never work when passing a Model. I’ve tried using your method inside my project, but the
“if (loaded[name] is IDisposable && …)” is never True for a Model file. Texture2D works fine, a Model doesn’t use the IDisposable interface. The Model asset should be bound to his own set of IDisposables so it will know which disposables he must unload when the asset is called for unloading. The current implementation simply skips Model assets completely.