Skip to main content
GameDev.net gamedev.net
🔒 Locked

XNA Drawable Components, what are the do's and dont's??

Started by ChrisPepper1989 Dec 13, 2008 at 9:22 AM 4 replies 12.3k views
Original Post
ChrisPepper1989
ChrisPepper1989
Hi, I've recently started learning XNA and i set out to make a re-usable framework and a game. And one of the things i've found myself asking is should i be using the DrawableGameComponent in the way i am. Quick summary of what i'm doing: I created a Behavior class and a Game Entity class. Game Entity holds all the core things that i think it should for now, position, size, rotation, type. As well as a list of the class Behavior that it will run through sending itself to like so:

public override void  Update(Microsoft.Xna.Framework.GameTime gameTime)
        {
            base.Update(gameTime);
            foreach (KeyValuePair<string, RBehaviour> element in behaviors)
            {
                behaviors[element.Key].Update(this);

            }
            
        }


And i added a similar thing for drawing, so that drawing can be done by behaviors as well. Its not the greatest set of classes in the world but at the minute it is not my goal to create the perfect classes for the job just ones that will work and allow me to learn from them. Summary of Problem: I first derived my Game Entity from Drawable component, and all game entities were put in the components list like so:

public RGameEntity(Game game) :  base(game)
        {
            game.Components.Add(this);
...


i first thought that i was utilizing an XNA feature but a friend pointed out that it wasn't really what Drawable Component was for. Doing so has also meant that ive had to throw around the Game variable so that dynamic creation of Game Entities is possible (which is annoying) and i also did some pretty horrible bodges with my sprite batch and other things to get things drawing. Which so far i haven't come up with alternate solutions to. I can post the code of these bodges for people confused on why i needed them but for now i find them quite embarrassing....its that bad... However i removed this dependency on Drawable game component, made the code nicer, got everything working again, and everything now ran a lot slower with a ridiculously bad frame rate. At the moment all entities are added to a static list of entities when they are created like so:

public RGameEntity() 
        {
            ....
            inMemory.Add(this);
...
}

and then ran through in static update and draws

public static void DrawAll(Microsoft.Xna.Framework.GameTime gameTime)
        {
            foreach(RGameEntity it in inMemory)
            {
                it.Draw(gameTime);      
            }
        }
        public static void UpdateAll(Microsoft.Xna.Framework.GameTime gameTime)
        {
            foreach (RGameEntity it in inMemory)
            {
                it.Update(gameTime);
            }
            foreach (RGameEntity it in toDelete)
            {///this will remove all possible references to the game entity and so GC will delete it
                it.behaviors.Clear(); 
                inMemory.Remove(it);
            }
        }


again im aware this isnt an elegant solution (im aware none of my code really is in this project so far) but i would of thought this would work the same as the drawable component cycle but with less optimization. However the whole thing ran a very considerable amount slower untill i stopped the fixed time step stuff with this line in the init:

this.IsFixedTimeStep = false;


And then everything ran as it did when they were derived from Drawable Component.. My Questions: -What does drawable component actually do, is it ultra optimized? -What should Drawable component be used for, should i carry on using it in this way and find a way around my bodges? -If not why did my project run so slowly the other way, im assuming its to do with how XNA will sacrifice draw loops for update loops, can someone shed some light on how that works aswell? Thank you for any constructive input =]
cyansoft
cyansoft
Your friend is correct. The drawable component should be used for systems, not individual game entities. Some examples would include a tile manager, or a scene graph (which looks like what you are doing), a sky system, a terrain system, a menu/ui system, etc. Some bad examples would be creating a drawable component for every instance of a unit in a RTS, or every instance of a tile, or every instance of a UI element, or every instance of a character or bullet or particle, etc.

The drawable component provides an update and draw method that is called for each update and draw automatically by the game loop. That's all it really does.

I'm puzzled to why your game ran slower when you made your entities not inherit the component class, but rather update and draw in a loop in your game's update and draw methods. There must be something else going on in your code that's causing the slowdown.

Just how many entities are we talking about? And are we creating and deleting entities during the middle of game play?
ChrisPepper1989
ChrisPepper1989
Thank you for clearing that up, im not actually doing a scene graph but i suppose some of the results are similar. So now i know to go down the route of the non drawable component version.

There is no creating and deleting of objects during gameplay at the moment, but there will be later. I was thinking perhaps the iteration of the lists and then the iteration of the Dictionaries is slow, because the game entity update looks like this:

public virtual void Update(Microsoft.Xna.Framework.GameTime gameTime)        {            if (this.kill)            {                toDelete.Add(this);                            }            else            {                foreach (KeyValuePair<string, RBehaviour> element in behaviors)                {                    if (behaviors[element.Key].isKilled())                    {                        behaviorsToKill.Add(element.Key);                    }                    else                    {                        behaviors[element.Key].Update(this);                    }                }                foreach (string element in behaviorsToKill)                {                    behaviors.Remove(element);                }                behaviorsToKill.Clear();            }        }

which is called by the code above that is iterating through a list! but surely that shouldnt be an issue.

There is 3 main entities at the minute, a space ship, a camera and the world, the space ship has 2 behaviors, the camera 3, and the earth 2. Then there are the stars which there is 1200 of, which have 2 basic behaviors, im sure taking these out would bring the speed right back up, my only quarrel with doing so, is why did it run faster when derived from drawable component then it does now.

And why does setting fixedTimeStep to false solve the problem? it makes me think that when the drawable component updates it somehow does it outside the fixed time step way? surely that's impossible! i suppose i should just work with fixedTimeStep set to false and update my objects by game time instead.

It is going to bother me though this damn drawable component thing! does drawable component do any kind of culling itself? because that would explain it.

Thanks for your help!
cyansoft
cyansoft
I personally try to design my update logic to not rely on a fixed time step. The reason for this is to prevent a slowdown if the game can only update 20 times this second opposed to 60 times (the default).

No, the draw component does not cull anything for you. It also updates and draws depending on if you are using a fixed time step.

First, we need to identify what area is taking so long: it is either the update, or the draw (or perhaps both). As to why a fixed update was slower, using a fixed time step means it will call update 60 times regardless if all the calls can be made in 1 second, thus the source of your slowdown.

What I would try to do first is change your draw call to draw nothing. Simply call base.Draw(gameTime) and then return.

Then create a private int for your game glass, initialize it to zero, and increment it in each Update. Print the gameTime's TotalGameTime after every 60 updates. Here's an example (not tested for compilation):

private int totalUpdates = 0;public override void Update(Microsoft.Xna.Framework.GameTime gameTime){  totalUpdates++;  if(totalUpdates % 60 == 0)    Debug.WriteLine("It is now: " + gameTime.TotalGameTime.TotalSeconds.ToString("0.00"));  // Your update logic here  base.Update(gameTime);}


Finally set the game's IsFixedTimeStep to true. Run the game for a ten or twenty seconds, and then check the output window. If the elapsed time is printing more than roughly 1 second intervals, your update logic is taking too much time to be run at 60 times per second.

It it does run at roughly 1 second intervals, your drawing logic is taking too much time.

My initial guess is to where the slowdown is that inMemory.Remove(it); call as it needs to step through the list one by one to find the element. But there is no way to tell for sure until you check if it's the update and/or draw calls that take up too much time.

If you check and do see that the update is taking too long, I would avoid removing elements from the inMemory list and rather change your foreach loop to simply skip over things that are considered dead.

Machaira
Machaira
Quote:
Original post by cyansoft
If you check and do see that the update is taking too long, I would avoid removing elements from the inMemory list and rather change your foreach loop to simply skip over things that are considered dead.

Agreed. It's almost never a good idea to create and destroy objects if you can avoid it and it's easy enough to avoid it by reusing "dead" objects. You'll have to balance memory usage with doing this, but unless you've got a ton of dead objects in the list it shouldn't be a problem.
Former Microsoft XNA and Xbox MVP | Check out my blog for random ramblings on game development
ChrisPepper1989
ChrisPepper1989
Thanks very much guys, ill give all this a test run tomorrow and get back to you! thank you for your time and the logic for recycling objects its something i was considering doing and so now i will definitely implement that later on. But at the minute there is no creation or deletion during game play so i will focus on finding the real route using cyansoft's method.

Thank you both!

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.