How to pass content through the project's structure efficiently?

Started by
5 comments, last by CC Ricers 9 years, 2 months ago

Hi there,

so following situation. I have a project which is structured something like this.

Game1 -> GameManager -> Module1-X (so multiple instances)

During runtime units are being spawned in each instance of the Module class. Those Units require spritesheets for animation. Let us say each unit has a "Move", "Attack" and "Die" state, each using a individual spritesheet. This makes three Texutre2D instances per unit and with 5 different units this adds up to 15 different Texture files. Those are being passed to the unit's constructor on creation.

As I am loading those textures in Game1's LoadContent method I have to pass them the entire way to the Module class which then uses those textures to instaciate units.

Game1.LoadContent(15x textures) -> GameManager(15x textures) -> Module(15x textures) -> Unit(3x textures)

Imo this has several disadvantages:

  1. As far as I can judge all those 15 textures are being held in memory of the Module classes the entire time. I would like to avoid that.
  2. Also passing 15 textures through the project's entire structure makes the classes unreadable and ugly.
  3. I've got the overall feeling that this is not the way things should be done here.

In order to avoid problem #2 I created a class, its only purpose is to hold all textures that are needed in Module.cs. I called the class AnimationCollection. It holds Texture2D variables and the according getters and setters. So now my Project looks like this.

Game1.LoadContent(new AnimationCollection(15x textures)) -> GameManager(AnimationCollection) -> Module(AnimationCollection) -> Unit(AnimationCollection)

While this does seem to solve problem #2 the other problems remain.

Are there other options on how to handle this problem? I could think of something like loading all content in the Module Class itself, but I never saw this before. I always load my stuff in Game1.LoadContent().

Any ideas, suggestions or expert knowledge is highly appreciated.

Thanks in advance!

Advertisement

Usally you use a resouce manager for handling shared resources like textures. Here's the basic setup of a resource manager (pseudo-code):


class TextureResourceManager : 
{
 HashMap<String,Texture> mTextures;
 HashMap<String,Integer> mCounters;
 TextureResourceManager () {..init members ...}

  Texture requestTexture(String id) {
    // texture already present ?
    Texture result = mTextures.get(id);
    // create new ?
    if(result==NULL) {
       result = loadTexture(id);
       mTextures.put(id,result);
       mCounters.put(id,0);
    }

   // increase counter by one
   mCounters.put(id,mCounters.get(id)+1);
   return result;
  }

  void releaseTexture(Texture tex) {
   // get id
   String id = tex.getId();
   // reduce counter
   int counter = mCounters.get(id)-1;
 
   // release shared resource ?
  if(counter==0) {
     ..free texture resources...
    mTextures.remove(id);
    mCounters.remove(id);
    } else {
      // still used by others
      mCounters.put(id,counter );
   }
 }
}


I agree with ashaman except I wouldn't load on demand, rather preload all textures on construction into the map. This way there is no delay when first displaying that texture. On xbox 360 for example disk access can be dog slow, which can mean a stuttery game for the first few seconds if you load on demand.

I'd also question the wisdom of having so many small textures. Why not combine all frames of animation for one unit into a single texture? Or even all frames for multiple units in one texture? Reducing the number of textures will improve your ability to batch draw calls, with improved performance. In the limit, if every sprite in your game can fit in a single texture then you'll never need to switch textures at all. That's unlikely of course, and deciding what to combine into textures for maximum benefit is very much dependent on your content and how it's used. But you can potentially gain in both performance and simplicity by merging assets.

Visit http://www.mugsgames.com

Stroids, a retro style mini-game for Windows PC. http://barryskellern.itch.io/stroids

Mugs Games on Twitter: [twitter]MugsGames[/twitter] and Facebook: www.facebook.com/mugsgames

Me on Twitter [twitter]BarrySkellern[/twitter]

Oh, and since this is for XNA you might look into using the SpriteFont generator for sprites generally. I don't know what it's like nowadays in XNA but I think the one in the native DirectXTK is basically the same. You can coopt the same functionality to pack any sprites you like into a 'spritefont' file, they don't have to be letters. Then you draw single frames by using the appropriate single-character 'string' for the sprite in question. It's a clunky API for the task because it annoyingly assumes you'll be drawing text, but it'll do the job effectively and saves you having to write your own sprite-sheet packing tools.

Visit http://www.mugsgames.com

Stroids, a retro style mini-game for Windows PC. http://barryskellern.itch.io/stroids

Mugs Games on Twitter: [twitter]MugsGames[/twitter] and Facebook: www.facebook.com/mugsgames

Me on Twitter [twitter]BarrySkellern[/twitter]


In order to avoid problem #2 I created a class, its only purpose is to hold all textures that are needed in Module.cs. I called the class AnimationCollection. It holds Texture2D variables and the according getters and setters. So now my Project looks like this.

That seems reasonable.


Are there other options on how to handle this problem? I could think of something like loading all content in the Module Class itself, but I never saw this before. I always load my stuff in Game1.LoadContent().

If Module is a DrawableGameComponent, then it could load the textures in its LoadContent


As far as I can judge all those 15 textures are being held in memory of the Module classes the entire time. I would like to avoid that.

Why? All you're storing is references to the textures. The textures obviously have to be loaded, since they're being used to draw the Units. The memory impact of 15 texture references is pretty insignificant.

Keep in mind that textures loaded by the same ContentManager object stay cached, so all your game Units that use the same texture will point to the same data in memory, as phil_t has mentioned. Your custom Module class just keeps references to the data loaded by ContentManager.

I agree with ashaman except I wouldn't load on demand, rather preload all textures on construction into the map. This way there is no delay when first displaying that texture. On xbox 360 for example disk access can be dog slow, which can mean a stuttery game for the first few seconds if you load on demand.

Yes, and this was true also because calling the GC to re-allocate megabytes of data was more expensive, and creates more latency. It is ideal to do this in transitional parts of your game such as a loading screen.

On most PC's it's not as big of a deal but it's still a good practice to pool your resources. Sometimes creating new data on demand can't be avoided, as with procedurally generated content. But you can still pre-allocate some data and keep re-writing to it as new content is generated.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

This topic is closed to new replies.

Advertisement