6
\$\begingroup\$

I'm making a SpaceWar!-esque game using XNA. I want to limit my ships to 5 active bullets at any time. I have a Bullet DrawableGameComponent and a Ship DrawableGameComponent. My Ship has an array of 5 Bullet.

What is the best way to manage the Bullet textures? Specifically, when should I be calling LoadTexture? Right now, my solution is to populate the Bullet array in the Ship's constructor, with LoadTexture being called in the Bullet constructor. The Bullet objects will be disabled/not visible except when they are active. Does the texture really need to be loaded once for each individual instance of the bullet object? This seems like a very processor-intensive operation.

Note: This is a small-scale project, so I'm OK with not implementing a huge texture-management framework since there won't be more than half a dozen or so in the entire game. I'd still like to hear about scalable solutions for future applications, though.

\$\endgroup\$

3 Answers 3

8
\$\begingroup\$

First of all: how you do it doesn't really matter. Just do the very simplest thing that could possibly work. My current favourite way is with static methods in each class (ie: bullet, ship), like this:

static Texture2D myTexture, myOtherTexture;
// Call me from Game.LoadContent:
public static void LoadContent(ContentManager content) { content.Load /* ...etc */ }

(This is perfectly fine for simple games where you only have a single ContentManager instance and never call Unload.)

Being able to toss in simple systems like this is one reason why I don't think it's a good idea to use DrawableGameComponent.

As has been mentioned a couple of times: XNA's ContentManager will share instances of loaded content (like textures). So after the first time you load it, subsequent loads will give you back the same texture with no cost for loading.


Anyway - it doesn't really matter how you load your content. What matters (and what you are asking) is when?

The answer to that is simple: Load your textures when your game starts! (Or behind loading screens or other the-game-isn't-animating screens if necessary.) If you cannot do this, the correct solution is to load your textures in a worker thread.

When you load a texture (a synchronous operation - it blocks the calling thread), you take a hit pulling the data from disk, a hit to decode it, a hit to send it to the GPU, and a hit if you trigger the garbage collector (blocks all threads, and is especially bad on Xbox 360).

In terms of the game loading, loading a texture is a very fast operation. In terms of the 16ms you have to calculate and render each frame at 60FPS, loading a texture is agonisingly slow.


Bill, in comments, asked for some resources on ContentManager. I am obviously biased, but I think some of my answers (including this one) contain some pretty good information and hint at some best practices:

And one from XNA lead Shawn Hargreaves: If ContentManager doesn't suit your resource-management needs, you should derive from ContentManager and override Load. This way you can implement whatever asset tracking you like, without making ugly custom wrappers.

\$\endgroup\$
1
  • \$\begingroup\$ "The answer to that is simple: Load your textures when your game starts!" Not sure if this fully answers the question "Does the texture really need to be loaded once for each individual instance of the bullet object?" because how would you load every bullet at the start of the game when you don't know how many would be fired? \$\endgroup\$
    – MetaGuru
    Feb 18, 2022 at 2:24
5
\$\begingroup\$

The simplest texture manager has one function: GetTexture. GetTexture checks its cache for a texture. If the texture isn't cached, it loads the texture. Then it returns the texture. Kapow! Done!

That is sufficient for most small-scale indie games I've seen. You just don't have enough content for this to ever be a problem.

One downside is that this can result in loading hiccups during gameplay. Quick fix: just call GetTexture with all the textures you expect to use during a "loading" section. Another possible fix: when you "load" a texture, hand a functional blank texture handle back, then start loading in the data asynchronously in the background (in another thread or with asynchronous file loading calls) and fill it in once it's done. This way you avoid the load hitches, but things can look a bit weird until textures are filled in. If you want to implement this, I'd recommend a GetTextureAsync function to call when asynchronous loading is acceptable (which in my experience will be only a few places, but those places may account for the majority of the textures in your game.)

Another downside is that you will, eventually, keep all the textures in your game in memory at once. This can also be easy to fix - if your game is divided into levels, just clear the entire cache between levels and reload everything. A somewhat more complex solution is to throw away textures after they've been unused for some number of seconds. This works great for large streaming worlds with unknown resources needed at each point (like MMOs - you never know what armor you're going to see next) but it does mean there's some lag between when a texture is unneeded and when the memory is actually freed.

If you want a more complex texture manager, you can start having formal concepts of blocks of textures that need to be preloaded. Each level can have a texture corpus, and when you change levels, you explicitly throw away the old corpus and load in the new one. Couple this with on-demand loading for unexpected textures, and a delay for deallocating those, and you're pretty much set for quiet some time.

As an example of this, in a game in a large contiguous world you might break the world into areas, then come up with a corpus for each area. Load textures for all adjacent areas to the player while throwing away textures from further away. (With refcounting, of course - if every area uses "grass", then there's no reason to store multiple copies of "grass".)

But meanwhile, back in reality, you'll probably be just fine with GetTexture. :)

\$\endgroup\$
5
  • \$\begingroup\$ Can you comment as to whether each entity needs to load the texture? (i.e., 5 Bullet = 5 LoadTexture calls) or do I only need to load once, then I can share that for all things that will use that texture? \$\endgroup\$
    – Bill
    Mar 4, 2011 at 6:24
  • \$\begingroup\$ If you go through a GetTexture manager, you'll end up with five GetTexture calls and one LoadTexture call. I'm assuming this all works the same as OpenGL, however - I've never used XNA itself - but I'd find it quite unbelievable that you couldn't re-use textures. That's kind of a fundamental of modern graphics :) \$\endgroup\$
    – ZorbaTHut
    Mar 4, 2011 at 8:54
  • 1
    \$\begingroup\$ XNA's built-in ContentManager already reuses loaded content, including textures. Caching, if you like. Don't implement a caching layer over the top! To do fancy stuff like granular caching and unloading between levels, create multiple instances of ContentManager or customise it. \$\endgroup\$ Mar 4, 2011 at 11:47
  • \$\begingroup\$ @Andrew Russell Do you have any links to good resources discussing the ContentManager and best practices when using it? \$\endgroup\$
    – Bill
    Mar 4, 2011 at 15:29
  • \$\begingroup\$ @Bill Rather than try and squash the info into a comment, I've added a bit more information to the end of my answer. \$\endgroup\$ Mar 6, 2011 at 13:47
2
\$\begingroup\$

If the texture is always the same then you should load the texture once in the game life, for example in the Ship class once it´s LoadContent is called.

***I´m not sure, but I think XNA loads the texture once, and the other calls to the load method will just use the memory loaded texture. Even if that is true, I think calling that method once is beter.

\$\endgroup\$
3
  • \$\begingroup\$ What do you mean "always the same"? Each bullet will use the same texture, and this texture does not change. So I only need to load it once and store a reference to it, then I can pass it to each of my Bullet objects? \$\endgroup\$
    – Bill
    Mar 4, 2011 at 6:36
  • \$\begingroup\$ He means what ZorbaTHut is saying in how GetTexture will cache the texture. XNA manages this automatically for you, so just call Content.Load<Texture>(textureName) in the LoadContent() method of your DrawableGameComponent, and XNA will handle the rest. If you are concerned about pre-loading the texture, then you should call Content.Load<Texture>(textureName) at the point at which you wish the texture to be loaded. This may be useful if you dynamically instantiate a DrawableGameComponent mid-game which uses a new texture and wish to eliminate the risk of the possibile of garbage collection. \$\endgroup\$ Mar 4, 2011 at 9:56
  • \$\begingroup\$ @michael.bartnett but at runtime when a bullet is fired during active game play you are already beyond the LoadContent() stage yet you are creating a new bullet sprite, this is what OP is asking and I don't think anyone has clearly answered it yet... (10 years later lol) \$\endgroup\$
    – MetaGuru
    Feb 18, 2022 at 2:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .