6
\$\begingroup\$

In the game state management example at the App Hub, they say that if you want to use components in the example you can extend the GameScreen to host other components inside itself.

I'm having a very hard time trying to tie this up. I tried extending the GameScreen class by adding a public property of public List<DrawableGameCompnent> components { get; set; } and then add my components to that list when I initialize the current screen as well as looping over the components in the LoadContent, Update and Draw methods. However, this doesn't feel like the correct way to go - mainly because it doesn't work when I get to the implementation of my GameplayScreen.

Any thoughts?

\$\endgroup\$

3 Answers 3

2
\$\begingroup\$

Edit: I was way off track and rethought my answer.

You're on the right track with each screen having a collection of components. In fact, there's a class just for that:

GameComponentCollection

Each screen can implement their own list the same as Game does. The screens shouldn't need to manipulate very much in other screens or the base game - they should be relatively confined. Things like Units, Map, Player, Lights, Cameras, Physics, Timers - they all go in the screen component collection.

Stuff in the Game.Components list are usually global functionality - ScreenManager, Input, Settings - things that get read from but not manipulated.

Wall of psuedo-ish code. I stripped down the AppHub example - figured it would be easier to add things in as I need functionality rather than start with a giant codebase - easier to learn that way for me.

class MyGame : Microsoft.Xna.Framework.Game
{
    GameScreenManager _gameScreens;

    // I make this static, seems easier?  Mine doesn't do much 
    // other than provide some helpers and shortcuts
    static InputManager _input;     
    public static InputManager Input { get { return _input } }

    public void MyGame()
    {
        // ... graphics initialization ...
        _gamescreens = new GameScreenManager();
        _gameScreens.Push(new MenuScreen(_gameScreens, _graphicsDevice));
    }
}

class GameScreenManager : Microsoft.Xna.Framework.DrawableGameComponent
{
    LinkedList<GameScreen> _screens;  // Not a 'stack', but we can use it as such

    public void Push(GameScreen screen)
    {
        _screens.AddFirst(screen);
    }

    public void Pop(GameScreen screen)
    {
        _screens.RemoveFirst(screen);
    }

    // ... Update is the same as draw... just updating() ...

    /// <summary>
    /// Draw the stack of game states
    /// </summary>
    public override void Draw(GameTime time)
    {
        // Here we just draw the top node, then recursively step down while
        // the screen is not BlockDraw.  This lets a screen hide or partially
        // let anything below it display (ie, a PauseScreen only partially
        // hides the GamePlayScreen
        _Draw(_screens.First, time);
    }

    /// <summary>
    /// Recursively draw the game state stack
    /// </summary>
    private void _Draw(LinkedListNode<Screen> node, GameTime time)
    {
        node.Value.Draw(time);

        // Only draw the screen below if the current one is not blocking
        if (!node.Value.BlockDraw)
           _Draw(node.Next, time);
    }
}

abstract class Screen
{
    // Game Screen Components (UnitManager, Lights, Sound, Cameras, Player, etc...)
    // These ones can implement IUpdateable and IDrawable, and the manager here
    // handles that
    // This could easily be GameComponentCollection instead - I just wrote my own
    // as I wanted to change a few things in it.
    protected GameScreenComponentManager _components;

    // Block updates to screens below.   Override in child classes if different
    public virtual bool BlockUpdate { get { retrun true; } }

    // Block draws to screens below.  Override in child classes if different
    public virtual bool BlockDraw { get { retrun true; } }

    // In the screen constructor create its own SpriteBatch, it's 
    // own SoundManager to handle playing sounds, it's own Player class, etc.
    // In this way, levels get easier too - each level is it's own screen.
    // OnLevelFinish just instantiates the next, pops the current from the manager,
    // and pushes on the new one.
    public Screen(GameScreenManager _screenManager, GraphicsDevice device, ...Other Dependancies...)
    { }

    public abstract void Initialize();
    public abstract void Update(GameTime time);
    public abstract void Draw(GameTime time);
}

So this is a simplified outline of the key parts.

GameScreens can create and push new screens onto the stack, and also remove themselves. Ie, then Menu Screen can push on a Settings Screen or a GamePlayScreen.

As commented, screens manage their own resources and are passed the dependencies needed to create these resources (or, register some dependencies in the service locator and just pass that into each screen). You can even create a new XNA ContentManager for each screen - leave one accessible back down in the Game for shared assets (but only a few). That way, when a screen is removed, in it's Dispose it releases only that screen's loaded assets.

\$\endgroup\$
2
  • \$\begingroup\$ So it's my, for example, SpriteManager that should implement these two interfaces or would it be my GameplayScreen? I must fail to see how this will simulate the whole component ordeal. \$\endgroup\$
    – Zolomon
    Mar 12, 2011 at 8:07
  • 1
    \$\begingroup\$ Wall of code, hope it's helpful. \$\endgroup\$
    – Leniency
    Mar 12, 2011 at 20:29
2
\$\begingroup\$

Chaps,

It is certainly possible (and common) to host DrawableGameComponents 'inside' other classes. Your container class can call their Initialize, Update and Draw methods directly (as described in Zolomon's first post I think).

It is not the case that DrawableGameComponents automatically add themselves to the Game component collection unless you do this explicitly. Some people choose to do this in derived classes immediately on creation, but it is not required.

This leads, therefore, an alternative approach to GameScreen containment whereby you can maintain the list of child DrawableGameComponents inside the GameScreen class and simply add and remove them from the main Game.Components collection as the screen is activated and deactivated.

This is the approach used to some extent in the AppHub Ninja game sample (which uses an updated version of the original gamestate classes I think).

Hope this info is useful.

\$\endgroup\$
1
  • \$\begingroup\$ Very - edited my answer for correctness. \$\endgroup\$
    – Leniency
    Mar 14, 2011 at 16:59
0
\$\begingroup\$

So what I did was that I made my SpriteManager derive from DrawableGameComponent and in the LoadContent of my GameScreen I add it to my game's components list with the Game.Components.Add() call and set its Enable and Visible settings like I want them.

Then I handle the Enable & Visible properties of my SpriteManager manually inside the GameScreen class so that if I bring up another screen I can turn the update or draw calls off.

Like Leniency said, DrawableGameComponents are global and will continue to draw on top of everything, so it has to be taken care of manually. I don't know if this is a good way to go but it seems to work.

Though, I don't understand how to make use of the IUpdateable and IDrawable interfaces.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ What all is SpriteManager responsible for? Does it manager a list of all loaded sprite assets, or does it simply load and draw them? Having screens try to manage resources below them sounds like it's asking for trouble later on. What happens when the SpriteManager has loaded things for multiple screens and you need to remove only part of them? Editing my answer above to try and clear things up (some things they make sense in my head, not so sure when put into words). \$\endgroup\$
    – Leniency
    Mar 12, 2011 at 19:47

You must log in to answer this question.

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