Sign in to follow this  

[XNA] Too Many Sprites?

This topic is 2340 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites
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 [img]http://public.gamedev.net/public/style_emoticons/default/wink.gif[/img]

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 [i]the whole [/i]gameworld. If you use just a simple screen without any scroll, just set the size and position like this:

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


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.

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


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 [img]http://public.gamedev.net/public/style_emoticons/default/smile.gif[/img]

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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#:

[url="http://www.gamedev.net/topic/532672-allocating-particles-in-memory-at-run-time-not-slow/"]http://www.gamedev.net/topic/532672-allocating-particles-in-memory-at-run-time-not-slow/[/url]

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...

Share this post


Link to post
Share on other sites
+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?

Share this post


Link to post
Share on other sites
@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!

Share this post


Link to post
Share on other sites
[quote name='BomberJacket' timestamp='1311014979' post='4836942']
@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!
[/quote]


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?

Share this post


Link to post
Share on other sites
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
[code]
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);
}[/code]

GameObject.cs
[code]
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);
}
[/code]

Share this post


Link to post
Share on other sites
Finally figured out an easy fix. Implemented IDisposable on GameObject and call Dispose() right after removing object from arrays. Probably not as elegant as a resource pool, but I can't guarantee all projectiles will be identical in the future. Regardless, my ship can now repeatedly fire 300 projectiles a second without crashing the game after any period of time (although with tremendous slowdown if firing from the bottom of the screen).

Thanks to everyone who helped, I really appreciate it. Moral of the story: IDisposable is your friend. Well, sometimes anyway.

Share this post


Link to post
Share on other sites
Cool, may I ask you how you use the Console.WriteLine in your xna game? I didn't even now that it was possible, but it seems to be a great way to debug [img]http://public.gamedev.net/public/style_emoticons/default/smile.gif[/img]

Share this post


Link to post
Share on other sites
A very easy way to use reuse resources is to make the index you want to remove unuseable (set it to null, add a flag, or whatever works for you).
At the same time, push the index to an array/list/vector that will hold all the "free" indices.
Now each time you want a new object, check first if you have a reuseable index, and if so grab it (and remove it so it wont be used again) and fill the object at that index with your data.

Share this post


Link to post
Share on other sites
It sounds like the original object you are creating still has an existing reference somewhere, which is the real problem. You'll probably still run out of memory just at a much later time now that you are getting rid of most of the object's stuff.

Where do you call Create and Destroy?

Share this post


Link to post
Share on other sites
[quote name='falcon93' timestamp='1311063130' post='4837256']
Cool, may I ask you how you use the Console.WriteLine in your xna game? I didn't even now that it was possible, but it seems to be a great way to debug [img]http://public.gamedev.net/public/style_emoticons/default/smile.gif[/img]
[/quote]

Yeah, it shows up in the Output window after the startup messages finish. By default that won't show up in the editor unless the game is running, so you have to enable it if you want to check it afterwards. Can't remember off the top of my head exactly which checkbox you need to hit, but Google will give you an answer there.

@Way2Lazy2Care
For now, Destroy is only called in Projectiles when they exceed the screen's bounds. Create is mostly a fancy wrapper for Game.AddObject, so if I "Create" something from Game, I'll use AddObject instead. That said, Game calls AddObject on Ship at the start, and Ship calls Create on Projectiles to fire its weapon. Could you elaborate more on the existing reference you mention? If it's a problem, it's one I'd prefer to deal with when I'm not working on other facets of the game.

Share this post


Link to post
Share on other sites

This topic is 2340 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this