ContentManager

Introduction

Content managers provide functionality for organizing and caching game assets. They can be thought of as a "bucket of assets". When you create an asset through the Content Manager, you give it a name (usually its file name) and decide which Content Manager it goes into. Later if the same name appears, the FlatRedBall Engine will return a reference to that asset instead of loading it from disk. Although MonoGame has a content manager class by the qualified name of Microsoft.Xna.Framework.Content.ContentManager, you do not have to ever explicitly interact with this class when using FlatRedBall. Instead, you simply work with content managers through the FlatRedBallServices class.

Content Manager and the FlatRedBallEditor

If you are using the FlatRedBall Editor, you may not have to directly interact with the ContentManager object. For example, if you add a .png file to the FlatRedBall Editor, it will automatically load the file in generated code using the ContentManager. However, if you would like to work with content in code, or if your game requires advanced handling of content then you may need to work with the ContentManager in custom code.

What is "caching"?

Computer memory can exist in a variety of locations: On the hard disk, in RAM, or even on the hard disks of other computers which you can access through the Internet (as you are currently doing when viewing this page). Not all types of memory can be accessed with the same speeds. Getting data through the Internet is very slow compared to data that is sitting on your local hard disk, and accessing data on the hard disk is much slower than accessing data in RAM. When the same data needs to be accessed multiple times, making a copy of it and moving it to faster memory can help improve performance. Content such as Textures and Models are copied from the hard disk to RAM to speed up future access to this memory - as well as to prevent duplicate memory in RAM.

What is a Content Manager?

Content managers simplify the loading of new assets as well as help organize existing assets. Content managers in FlatRedBall store references to a variety of assets. The most common type of reference held by content managers is of type Texture2D. Any FlatRedBall method which can potentially load a Texture2D works with FlatRedBall's internal content managers. Content managers are also useful for organizing data. Any data which has the same lifespan - such as textures, geometry, and sound files in a given level - should belong to the same content manager. When the level ends, simply removing that content manager unloads all memory associated with the level. Content managers make memory management and game state changes much easier.

Using Content Managers

If you have programed a FlatRedBall application which displays a texture (such as the red ball texture on a Sprite) then you have worked with content managers - although you may not realize it. Any method which takes a string file name for a Texture2D also takes a string for a content manager name. Some methods have overloads which do not require a content manager string argument, but those simply use the default "Global" content manager. You may be familiar with the SpriteManager's AddSprite method:

Sprite someSprite = SpriteManager.AddSprite("redball.bmp");

This method has an overload which allows the content manager to be specified. Calling the one-argument version is the same as passing "Global" as the second argument:

Sprite someSprite = SpriteManager.AddSprite("redball.bmp", "Global");

The Global content manager is used by default by all methods which load Texture2D's when a content manager is not explicitly supplied in the argument list. To specify a different content manager, simply provide the string of the new name - there is no need to instantiate any instances as this is all handled internally. Loading the same asset using the same content manager does not result in multiple copies of the asset in memory and the hard disk is only accessed once. The following code creates only one copy of the redball.bmp texture in memory.

SpriteManager.AddSprite("redball.bmp"); // in Global by default
SpriteManager.AddSprite("redball.bmp", "Global");
FlatRedBallServices.Load<Texture2D>("redball.bmp", "Global");

While the following creates three separate copies of the redball.bmp texture and also the hard disk is accessed three times:

SpriteManager.AddSprite("redball.bmp", "Some Content Manager");
SpriteManager.AddSprite("redball.bmp", "Other Content Manager");
SpriteManager.AddSprite("redball.bmp", "other content manager"); // case sensitive!

Or another way to look at it is:

Texture2D firstTexture = SpriteManager.AddSprite("redball.bmp", "Some Content Manager");
Texture2D secondTexture = SpriteManager.AddSprite("redball.bmp", "Other Content Manager");
Texture2D thirdTexture = SpriteManager.AddSprite("redball.bmp", "other content manager"); // case sensitive!

if(firstTexture == secondTexture)
{
   // This won't get hit
}

if(secondTexture == thirdTexture)
{
   // This also won't get hit.
}

Content Manager Code Sample

The following code simulates the creation and destruction of 3 levels. Levels can be loaded and unloaded by pressing the 1, 2, and 3 keys. The text object which is created displays the loaded content and the Sprites themselves display the loaded Sprites.

 // Add this using statement:
 using FlatRedBall.Graphics;

 // in class scope
 Text debugText;

 bool[] mLevelLoaded;
 SpriteList mLevel1Sprites;
 SpriteList mLevel2Sprites;
 SpriteList mLevel3Sprites;

 // replace Initialize with this:
 protected override void Initialize()
 {
     FlatRedBallServices.InitializeFlatRedBall(this, this.graphics);

     base.Initialize();

     debugText = TextManager.AddText("");
     debugText.Y = 13;
     debugText.X = -16;
     debugText.Scale = debugText.Spacing = .5f;

     mLevelLoaded = new bool[3];

     mLevelLoaded[0] = false;
     mLevelLoaded[1] = false;
     mLevelLoaded[2] = false;

     mLevel1Sprites = new SpriteList();
     mLevel2Sprites = new SpriteList();
     mLevel3Sprites = new SpriteList();

 }

 // replace Update with the following:
 protected override void Update(GameTime gameTime)
 {
     FlatRedBallServices.Update(gameTime);

     if(InputManager.Keyboard.KeyPushed(Keys.D1))
     {
         if(mLevelLoaded[0] == false)
         {
             Sprite sprite = SpriteManager.AddSprite("redball.bmp", "Level1");
             sprite.X = 5;
             mLevel1Sprites.Add(sprite);
             
             mLevelLoaded[0] = true;
         }
         else
         {
             mLevelLoaded[0] = false;
             FlatRedBallServices.Unload("Level1");
             SpriteManager.RemoveSpriteList(mLevel1Sprites);
         }
     }
     else if(InputManager.Keyboard.KeyPushed(Keys.D2))
     {
         if(mLevelLoaded[1] == false)
         {
             Sprite sprite = SpriteManager.AddSprite("redball.bmp", "Level2");
             sprite.X = 1;
             mLevel2Sprites.Add(sprite);

             sprite = SpriteManager.AddSprite("redball.bmp", "Level2");
             sprite.Y = 5;
             sprite.X = -2;
             mLevel2Sprites.Add(sprite);
             
             mLevelLoaded[1] = true;
         }
         else
         {
             mLevelLoaded[1] = false;
             FlatRedBallServices.Unload("Level2");
             SpriteManager.RemoveSpriteList(mLevel2Sprites);
         }
     }
     else if(InputManager.Keyboard.KeyPushed(Keys.D3))
     {
         if(mLevelLoaded[2] == false)
         {
             Sprite sprite = SpriteManager.AddSprite("redball.bmp", "Level3");
             sprite.X = -3;
             mLevel3Sprites.Add(sprite);
             
             sprite = SpriteManager.AddSprite("redball.bmp", "Level3");
             sprite.Y = 2;
             sprite.X = 4;
             mLevel3Sprites.Add(sprite);

             sprite = SpriteManager.AddSprite("redball.bmp", "Level3");
             sprite.Y = 6;
             sprite.X = -4;
             mLevel3Sprites.Add(sprite);

             mLevelLoaded[2] = true;
         }
         else
         {
             mLevelLoaded[2] = false;
             FlatRedBallServices.Unload("Level3");
             SpriteManager.RemoveSpriteList(mLevel3Sprites);
         }
     }