[XNA] Too Many Sprites?

Started by
15 comments, last by Tom Sloper 12 years, 10 months ago
So, I've picked up XNA again after a long time with Unity, and I've run into an interesting problem. I'm doing a Shmup, and after roughly 1700 shots, the sprites for them are no longer being drawn. There's no warning messages, nothing in the console to indicate that something's gone wrong. I'm really at a loss as to what's going on here, though I've got an inkling that it has to do with the previous sprites not being properly destroyed. And given that its a shmup, "use fewer sprites" really isn't going to be an option.

Has anyone else had this problem or know what might be causing it? I've tried a few things already. The best idea seemed to be to use SpriteBatch.Dispose, but no matter where I put it or how I set things up it seems to always crash my game.
Advertisement
This might not solve your initial problem but, try re-using none active bullets ( bullets that is offscreen ).
That way you could check if their already is an inactive bullet before you allocate a NEW one :)
I've never encountered this problem when I programmed my 2D game AirDefence, which was cannon game where you was supposed to shoot down enemy aircrafts. In that game I used a cannon who was going to shoot down the aircrafts. However, I never had any problems with the amount of shots fired, as the game keept "deleting" shots outside the screen.

This is actually very easy to program, and I think that this may solve your problem.

I guess that you use a List for all your shots? If so, this will be peace of cake wink.gif

Start by creating a rectangle that will represent your whole "game world". I don't know if your game screen can scroll in different directions, if so, you'll have to fix so your rectangle covers the whole gameworld. If you use just a simple screen without any scroll, just set the size and position like this:


rectangle.X = 0;
rectangle.Y = 0;
rectangle.Width = 1280; // Or whatever your screen width is
rectangle.Height = 800; // Or whatever your screen height is



After that, you check if the bullets intersects with this rectangle. This also require that the bullets have a rectangle. The first step of doing this, is to loop through all bullets, then check which ones that is outside the rectangle, and finaly deleting them.


for (int i = 0; i < bulletList.Length; i++)
{
if (!bulletList.Instersects(gameworld_rectangle))
{
bulletList.RemoveAt(i);
break;
}
}



This will delete all bullets that is outside the game screen, and hopefully solve your problem. If it doesn't, just shout and we'll see if we can solve it in another way smile.gif
Thanks for the advice.

@Moonkis Reusing bullets sounds good, although I plan on having bullets with various properties on them. Will see how this pans out

@falocn93 I am removing bullets that travel outside the screen dimensions, although again, C#'s garbage collection doesn't seem to be kicking in before the sprite limit is reached.

One thing I forgot to add is that I'm adding bullets in as Components. Maybe there's a certain way I have to remove them?
As Moonkis says, for particle systems you'll get substantially better performance if you pre-allocate them all and reuse the memory (bool active flags on particles). Here's a thread from a few years back that compares different methods of handling particle allocation in C#:

http://www.gamedev.net/topic/532672-allocating-particles-in-memory-at-run-time-not-slow/

The basic gist is that you can get 50% more particles on the screen at the same frame-rate if you pre-allocate particles and never delete them. If you plan on deploying to Xbox you want to minimize garbage collection as well. You need to set a ceiling on number of shots anyway...
You should only need one sprite and redraw it for each of the particles.

It's too hard to say much without you giving any code.
+1 To EJH

Also, remember that the GC is non-deterministic. You cannot guarantee when it will run.

Do you have 1700 bullets attempting to draw at the same time?
@EJH Thanks, I'll take a look at this later. Seems like an approach that could work well, although not all of these Projectiles are going to have the same behavior or sprite.

@way2lazy2care Will post some code when I get home. I'm adding the Projectiles individually as Components, and each one has its own Draw call.

@JSelf No, just 1700 total. The ones that go off the screen get removed from Components, so their Draw loops shouldn't be called anymore. When it hits 1700, I hit the Fire key and the Log says that Projectiles are being created, but I don't see any being drawn on the screen.

Thanks for the all the responses!

@EJH Thanks, I'll take a look at this later. Seems like an approach that could work well, although not all of these Projectiles are going to have the same behavior or sprite.

@way2lazy2care Will post some code when I get home. I'm adding the Projectiles individually as Components, and each one has its own Draw call.

@JSelf No, just 1700 total. The ones that go off the screen get removed from Components, so their Draw loops shouldn't be called anymore. When it hits 1700, I hit the Fire key and the Log says that Projectiles are being created, but I don't see any being drawn on the screen.

Thanks for the all the responses!



And no other errors happen beside the sprites not drawing? Have you run it in debug and checked it with breakpoints to make sure the stuff all still exists?
OK wow, this is apparently a memory issue. I waited after the shot sprites stopped drawing and then the screen started flickering and the ship disappeared. Then the whole thing crashed and I got an out of memory error. I guess things are being created faster than they're being released?

Here's a few code samples. I'm deriving most things that are drawn in the scene from a parent class called GameObject. GameObject derives from DrawableGameComponent. So, in my Game file, I just use Components.Add on whatever GameObject I want to add and add the object to an array of GameObjects to check collisions en masse (if two objects collide, they each call a function in GameObject called OnCollision). I wanted to simplify things, so everytime I add/remove a GameObject to the scene, I do it through the Game.AddObject and Game.RemoveObject (Game has a static instance so the GameObjects themselves can call these as well, mostly to properly destroy themselves).

I'm posting all the code related to creating and deleting GameObjects. I'm guessing something isn't being removed from an array or something, but I'm at a loss as to where.

Game.cs

public void AddObject(GameObject objToAdd)
{
spriteCounter++;
Console.WriteLine(spriteCounter);
gameObjects.Add(objToAdd);
Components.Add(objToAdd);
}

public void RemoveObject(GameObject objToRemove)
{
spriteCounter--;
Console.WriteLine(spriteCounter);
gameObjects.Remove(objToRemove);
Components.Remove(objToRemove);
}


GameObject.cs

public virtual void Create(GameObject toCreate, int newDrawOrder = 5)
{
toCreate.DrawOrder = newDrawOrder;
Game.instance.AddObject(toCreate);
}
public virtual void Destroy(GameObject toDestroy, bool loudDeath = true)
{
if (loudDeath)
OnDeath();
Game.instance.RemoveObject(toDestroy);
}

This topic is closed to new replies.

Advertisement