2
\$\begingroup\$

Background:

I'm working on a control library for XNA to use in a couple game clients I've written. The way I've implemented Dialogs is to have them derive from the base XNAControl class.

XNAControl objects follow the Game Component model and are all derived from DrawableGameComponent. They are added to the Game.Components list as soon as the base class XNAControl constructor is called.

Problem:

Dialogs are popped up in game by instantiating an XNADialog object, unlike MessageBox.Show() which uses a static method. The constructor of XNADialog, in turn, instantiates a couple controls that are 'children' of the parent dialog control. Children are tracked by the dialog, and removed from the Game.Components list automatically when the Dialog is closed.

The problem with the model that I've created is that the child controls are added to the Game's components when they're constructed, so they start updating and drawing and thus show on the screen before the dialog is fully constructed. The case I'm trying to debug is where the labels for a dialog box will appear in the top left, and then move to the center of the screen a split second later once the Dialog is constructed.

What I've tried:

  • Separate constructor for base class XNAControl that doesn't add the control to the components when it's constructed. Instead, it relies on the child control (in this case dialog) to add itself to the Game.Components collection. This is what I have in place so far and it isn't working, which makes sense, because the labels are being added to the components collection prematurely anyway.
  • Initialized property of XNAControl. This property is set in the XNAControl.Initialize() method, and the base class XNAControl.Update and XNAControl.Draw return automatically if it is false. However, this isn't working either (also in place) because child controls have to call the base methods of update and draw before after they do their own drawing logic because otherwise some things would be drawn in the wrong order. I also don't want to have to check the Initialized property in every single child class I make because there are a lot of them and I'm looking for something more elegant
  • Moving the addition of the control to the components collection to the Initialize method. This didn't work because the control's initialize method didn't get called automatically because the control wasn't in the components collection yet.

Question:

What is the best way to keep the child controls from being updated and drawn prior to their parent control's construction? My only other ideas are:

  • Using a theading sync object, but this seems like overkill/unnecessary (or maybe it's exactly what I need?). I can't think of an elegant way to do it.
  • Finding a way to cancel any drawing that's been done by the SpriteBatch in the child classes, in XNAControl.Draw. I don't think this is possible though, and I can't find anything searching.
\$\endgroup\$
2
\$\begingroup\$

So, first of all, for anyone coming here from search reading your title, I'll answer how to "Cancel a SpriteBatch I've started drawing":

Basically: You can't. A SpriteBatch acts like a list of sprites, and that gets send out to the GPU when you call End (or immediately with SpriteSortMode.Immediate). But, unlike a List, there's no Clear method or similar to erase that list without sending its contents to the GPU.

So, one terrible option is to Dispose of your SpriteBatch and then recreate it. This is pretty horrible, structurally. It's also going to perform terribly.

A more correct alternative would be to re-implement your own version of SpriteBatch, or simply create a wrapper around it that does what you need. Keep in mind that there's nothing "special" about SpriteBatch - it's built on top of other parts of XNA and it's possible (although non-trivial) to create your own version.


But, really your question is "How do I sort out my wacky use of DrawableGameComponent?"

And the answer is: "Don't do that!"

When you get to the point where you're trying to do crazy things like "cancel" a sprite batch, or looking into threading synchronization mechanisms (note: they're not applicable here), consider, perhaps, that you've taken the wrong approach in the first place.

Again, much like SpriteBatch, the GameComponent system in XNA is entirely optional. XNA is designed so you can replace it with your own system. And, unlike with SpriteBatch, making your own replacement for the component system is trivial (as it basically boils down to a list of objects with Update and Draw methods).

In my experience, it's almost always the case that you'll want something slightly different to what XNA's component system does. And, in your case in particular, you want something very different - possibly a tree structure (instead of a list), where each parent control is directly responsible for drawing its children.

OR: Keep doing what you're doing and see if the Enabled and Visible properties on DrawableGameComponent do what you need (it's still not great, but this might be a quick band-aid solution to your problem).

\$\endgroup\$
  • \$\begingroup\$ In response to the first part about the SpriteBatch, I figured as much. I don't think I'm up to writing my own SpriteBatch so I'll leave it be :) I'm going to rethink my approach like you suggest. A tree structure is exactly what I'm looking for, and sort of what I'm already trying to do. I just got so locked in to the way it was already all set up in XNA land. \$\endgroup\$ – E. Moffat Aug 7 '14 at 16:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.