locked
Best coding practises with ContentManager and GraphicsDeviceManager ?

    Question

  • I hope someone can give me a decent explanation of this as it's something which I would of thought is pretty essential to writing anything in GSE but makes no mention of it in the documentation. I'm trying to understand the best way for any game elements to access the main games graphics and device, these need to be written in a way that I can give this routine to someone else and they work without the other person having to hard code aynthing in the code I give them. I believe there may of been a more obvious way to do this in beta 1 but I picked things up from beta 2 so some of the examples and documentation is referencing things which have since been taking out.

    My main confusion comes from the differences in the Windows Game and Spacewar templates. The standard Windows Game template, see below, defines graphics and content as private and so would be encouraging beginners to write everything in the main unit, not a wise practise.
     
    A new Windows game template

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager
    graphics;
        ContentManager
    content;
     
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this
    );
            content = new ContentManager
    (Services);
        }
     
    My understanding of the game services answers one of the questions, how to access the GraphicsDeviceManager without knowing its name. For this I would use IGraphicsDeviceService graphicsService = (IGraphicsDeviceService)game.Services.GetService(typeof(IGraphicsDeviceService)) (see Font.cs Init), but I need to pass a reference to the game i.e. public Class1(Game game). Okay, so now my game element can access the device and I can draw things, but to draw something I'll probably need to access the ContentManager and that's where I start banging my head on my keyboard.

    In the Spaceware example the ContentManager has been made private static, with a public static property so it can be accessed from other classes (see below).
     
    A new Spacewar game template

    partial
    class SpacewarGame : Microsoft.Xna.Framework.
    Game
    {
        private static ContentManager contentManager;
        private GraphicsDeviceManager graphics;

       
    public static ContentManager
    ContentManager
        {
            get
            {
                return
    contentManager;
            }
        }

        public SpacewarGame()
        {
            this.graphics = new Microsoft.Xna.Framework.GraphicsDeviceManager(this
    );
        }

        protected override void LoadGraphicsContent(bool loadAllContent)
        {
            base
    .LoadGraphicsContent(loadAllContent);
            if
    (loadAllContent)
            {
                contentManager = new ContentManager(Services);
            }
        }

    This allows the ContentManager to be accessed from the games class rather than a game instance. This is fine because they'll only ever be one instance of the game but straight away it leads to having to modify the code to match the class name of the game so I can't put my routine into the public domain and expect it to work with someone elses code.

    I'm not keen on using static, my preference would be to pass a reference to the game to the constructor of other classes and access the ContentManager via a public property. We're still back to the problem of having to know the class name of the game to do this ! Spacewar has to do this (see Font.cs Draw at the bottom, Spacewar.ContentManager). If it wasn't for that reference to Spacewar the code would be alot more portable.

    It feels like the Microsoft.Xna.Framework.Game class needs an accessible reference to the ContentManager and the problem would be solved. Maybe it does already ? Maybe everything will change in just a few days when the final version is released !

    Maybe people who've written stuff in beta 2 can share their experiences. Maybe I should stop saying maybe !

    Apologies if this has been covered before, it seems an obvious thing to cover but I couldn't find anything on it.


    Cheers,
    Terry

    Tuesday, December 05, 2006 3:49 PM

Answers

  • Here's a real simple solution that I am using:

    class MyGame : public Game
    {
        MyGame()
        {
           instance = this;
        }

        public ContentManager ContentManager
        {
           return content;
        }

        static public MyGame Instance
        {
           get { return instance; }
        }

        // do other properties here (like services)

        public GetService<T>()
        {
           return (T)services.GetService(typeof(T));
        }

        private static MyGame instance;
        private ContentManager content;
    }

    Now all of these variables are essentially global from anywhere using MyGame.Instance
    Tuesday, December 05, 2006 4:48 PM
  •  Jim Perry wrote:
    which I exposed as a property of the Game class.

    Jim, I have to admit I don't quite follow  Do you mean the Framework.Game class or your game class ? If you exposed the Framework.Game class then how and if it is your own class then don't you have to still have to make a reference to your game class which would change for every new project ? It might have something to do with the GameComponent but I hadn't looked at that, I thought that was one of those visual component tools that was being dropped after beta 1.

    Anyway, after my initial post I had a think and came up with this solution.

    public static class GameManagers
    {
        private static GraphicsDeviceManager
    graphics;
        private static ContentManager
    content;
        
        public static GraphicsDeviceManager
    Graphics
        {
            get { return
    graphics; }
            set { graphics = value
    ; }
        }

       
    public static ContentManager
    Content
        {
            get { return
    content; }
            set { content = value
    ; }
        }
    }

    Yeah, I know I said I didn't like statics  Here I'm putting both the graphics manager and content manager in a seperate class defined just before my own game class. Then in the game class constructor I'd have :

    GameManagers.Graphics = new GraphicsDeviceManager(this);
    GameManagers.Content =
    new ContentManager(Services);

    Then I can just reference GameManagers.Graphics and GameManagers.Content from anywhere else and the code still works with a new project without any changes.

    Anything wrong with this method ?

    Terry

    Tuesday, December 05, 2006 5:32 PM
  • Is there any reason you guys are opposed to just having all supporting classes of your games get passed a reference to the ContentManager and DeviceManager and whatever other references they need when they are initialized? Then there's no need to be exposing things in my game and my class constructor show what they need to work properly keeping things encapsulated in a much cleaner way.

    Also, using that method, when you remove a class from one game and use it in another, you won't have to restructure that game class to have the same exposed variables, properties etc. just to get a supporting class working again so object reuse is easier.

    Just kind of curious if there's some kind of detriment I'm not seeing by going about things in that way.


    Tuesday, December 05, 2006 8:52 PM

All replies

  •  Terry A. King wrote:
    It feels like the Microsoft.Xna.Framework.Game class needs an accessible reference to the ContentManager and the problem would be solved.

    Which is what I've done in my projects.  It seems to work fine for me.

    Tuesday, December 05, 2006 4:21 PM
  •  Jim Perry wrote:
    Which is what I've done in my projects.  It seems to work fine for me.

    Can you give an example of what you've done ?

    Tuesday, December 05, 2006 4:41 PM
  • Here's a real simple solution that I am using:

    class MyGame : public Game
    {
        MyGame()
        {
           instance = this;
        }

        public ContentManager ContentManager
        {
           return content;
        }

        static public MyGame Instance
        {
           get { return instance; }
        }

        // do other properties here (like services)

        public GetService<T>()
        {
           return (T)services.GetService(typeof(T));
        }

        private static MyGame instance;
        private ContentManager content;
    }

    Now all of these variables are essentially global from anywhere using MyGame.Instance
    Tuesday, December 05, 2006 4:48 PM
  • For now, I've just gone through my GameComponents' Game property to get to the content manager, which I exposed as a property of the Game class. Since you have to pass a Game object to a GameComponent you might as well use it.  For those cases where you need to get to the content manager from non-GameComponent code the above is probably fine.
    Tuesday, December 05, 2006 4:54 PM
  •  Jim Perry wrote:
    which I exposed as a property of the Game class.

    Jim, I have to admit I don't quite follow  Do you mean the Framework.Game class or your game class ? If you exposed the Framework.Game class then how and if it is your own class then don't you have to still have to make a reference to your game class which would change for every new project ? It might have something to do with the GameComponent but I hadn't looked at that, I thought that was one of those visual component tools that was being dropped after beta 1.

    Anyway, after my initial post I had a think and came up with this solution.

    public static class GameManagers
    {
        private static GraphicsDeviceManager
    graphics;
        private static ContentManager
    content;
        
        public static GraphicsDeviceManager
    Graphics
        {
            get { return
    graphics; }
            set { graphics = value
    ; }
        }

       
    public static ContentManager
    Content
        {
            get { return
    content; }
            set { content = value
    ; }
        }
    }

    Yeah, I know I said I didn't like statics  Here I'm putting both the graphics manager and content manager in a seperate class defined just before my own game class. Then in the game class constructor I'd have :

    GameManagers.Graphics = new GraphicsDeviceManager(this);
    GameManagers.Content =
    new ContentManager(Services);

    Then I can just reference GameManagers.Graphics and GameManagers.Content from anywhere else and the code still works with a new project without any changes.

    Anything wrong with this method ?

    Terry

    Tuesday, December 05, 2006 5:32 PM
  •  Terry A. King wrote:

     Jim Perry wrote:
    which I exposed as a property of the Game class.

    Jim, I have to admit I don't quite follow  Do you mean the Framework.Game class or your game class ?

    I meant the content manager member of the Game object:

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        ContentManager content;

        public Game1()
        {
            graphics =
    new GraphicsDeviceManager(this);
            content =
    new ContentManager(Services);
        }

    public ContentManager contentManager
    {
    get { return content; } }

    }

    In my GameComponents I get to it like this:

    ((Game1)this.Game).contentManager

    I haven't encountered any problems doing this so far.

    Tuesday, December 05, 2006 6:29 PM
  •  Jim Perry wrote:

    In my GameComponents I get to it like this:

    ((Game1)this.Game).contentManager

    Ahh, that's as I understood then. Part of my original question (which I didn't put across very well) was one of reusability . If you use this GameComponent in another project and you don't call it Game1 then your forced to change those references to it.

    Tuesday, December 05, 2006 8:12 PM
  • Yeah, there's probably a way around that, but off the top of my head I'm not sure how.
    Tuesday, December 05, 2006 8:36 PM
  • Is there any reason you guys are opposed to just having all supporting classes of your games get passed a reference to the ContentManager and DeviceManager and whatever other references they need when they are initialized? Then there's no need to be exposing things in my game and my class constructor show what they need to work properly keeping things encapsulated in a much cleaner way.

    Also, using that method, when you remove a class from one game and use it in another, you won't have to restructure that game class to have the same exposed variables, properties etc. just to get a supporting class working again so object reuse is easier.

    Just kind of curious if there's some kind of detriment I'm not seeing by going about things in that way.


    Tuesday, December 05, 2006 8:52 PM
  • I'm not opposed to it. I figured since I had to pass a reference to the game anyway, why not use it's members where appropriate. It seems strange to have to pass an object and a member of the object. If you have to use the ContentManager to load content, why wouldn't the game expose it? Making it a static object would also work, but since I have to pass the game object anyway...
    Tuesday, December 05, 2006 9:15 PM
  • Well, technically, you have to pass an object and a member of a derived object. The component needs a Game object, and doesn't know (or care) about MyAwesomeGame object other than that it has to be a derivative of Game. From a decoupling perspective, I have no problem with the idea of passing the ContentManager and GraphicsDeviceManager objects separately. The alternative feels clumsy to me.

     

    Tuesday, December 05, 2006 9:56 PM
  •  George Clingerman wrote:
    Is there any reason you guys are opposed to just having all supporting classes of your games get passed a reference to the ContentManager and DeviceManager and whatever other references they need when they are initialized?

    Using the current Windows Game template I guess that's the easiest thing to do. Feels kinda long winded and messy to be passing ContentManager and DeviceManager to every supporting class though. Just feels like the base class, Game, should be providing access to the ContentManager now that every game will essentially be using it. Actually wouldn't it simply be better for a the ContentManager and DeviceManager to be created for you when the Game class is constructed ? Keeps it alot simpler for beginners then, but advanced users still have the option of created additional managers if they need them.

    Terry

    Tuesday, December 05, 2006 10:25 PM
  • Terry

    I posted a similar comment to this in the XNA Framework forum (but without a code example, which was probably a mistake).

    Again, I'll state up front that I think XNA is *wonderful*.

    My point in my previous post is the same as yours: the framework doesn't go far enough to *enforce* good (or even consistent) practise so we all end up with different approaches.  This leads to unnecessarily hard-to-share code.  For example, I've created Font/FontManager and ImageMap/ImageMapManager 2D classes but I bet they won't work with your code as I've made ContentManager a service so it's accessible from a Game object:

    public SomeGame()

    {

    ...

    contentManager = new ContentManager(this.Services);

    this.Services.AddService(typeof(ContentManager), contentManager);

    ...

    // Elsewhere (don't like this long-winded call plus casting, by the way)

    ContentManager contentManager = (ContentManager)this.game.Services.GetService(typeof(ContentManager));

    }

    Although the Game and ContentManager classes are technically decoupled, my code is still coupled to the approach of treating ContentManager as a Game service so I'm no further forward.  Please don't critique this approach by the way as I consider it a temporary workaround until I discover how it should really be done.

    I'll think some more about the solution and post again but I think what we need are some well-defined interfaces for 'common' components such as ContentManager and GraphicsDevice (these classes don't implement any interfaces currently) and an abstract (i.e. replaceable) object factory with a default implementation provided by the framework.  This object factory would be accessible via the Game object.

    So, assuming my class has a reference to the Game object (game), we can say things like

    IContentManager contentManager = game.Factory.GetContentManager();  // singleton

    ISomeCommonObject someCommonObject = game.Factory.CreateSomeCommonObject(); // non-singleton

    When you construct Game, you provide your factory object if you have special requirements:

    MyGame myGame = new MyGame(new MyGameFactory());

    MyGameFactory would implement the IGameFactory interface from scratch or (much more likely) subclass the default framework factory implementation and override where necessary.

    What's 'common' will still be contentious but at least we'll be able to share code at some level.

    My hope is that something can be done ahead of RTM but this is pretty radical.  OK, maybe it's not pretty.

    Cheers.

     

    Tuesday, December 05, 2006 11:00 PM
  • Yet another reason to not mess with the game class exposing things that aren't currently exposed, but instead have your class constructor (or methods you're calling) ask for the object they need to be initialized. That's really the only way to guarantee that your code works in someone else's project. It's kind of the basis of OO.

    If everyone would take that approach instead of monkeying with what is there then the consistency would be there. Exposing properties isn't a "best practice" really. Now, should the XNA team expose those properties? Maybe since it seems like a lot of people are taking the time to do that, but they haven't yet so the practice I would recommend would be to NOT expose properties and objects of the Game but instead make sure your methods and class constructors don't make assumptions of pre-existing objects but instead ask for objects they need.


    Tuesday, December 05, 2006 11:11 PM
  • I'm not suggesting we mess with the Game class as individual developers - I agree this would be *bad*.  I'm suggesting that the current framework leaves things too open and the samples so far don't help - my suggestion was how, at a framework level and by the framework team, things could potentially be improved and in particular *enforced*.

    Your suggestion of parameterising constructors with all the objects they'll ever need is unrealistic and leads to hard-to-use classes - even if code-sharing were not an issue, I would not recommend this approach.  Not least, it assumes that I have references at hand for all the necessary parameter objects when I want to construct one of your objects - if I don't, how do I get them?  This leads to cross-cutting concerns, for example: I want to construct a Widget but I need to find a Blodger (which I don't have and don't care about) first.

    To fix the Blodger problem, maybe I go to a central factory object that provides one for me...  why can't the Widget do that, at least by default?

    Also, note that your approach is not *enforced* by the framework but is a loose pattern implemented (inevitably differently) by different developers - maybe you put ContentManager first, followed by GraphicsDevice and I do the reverse.

    And there is no way to guarantee any code will work in someone else's project.  I've even seen my own code not work in my own projects.

    Cheers.

    Tuesday, December 05, 2006 11:42 PM
  • I just mentioned the content manager issue as a possible mini-tutorial for the release party that the XNA team is having. Everyone did read about it, right?
    Wednesday, December 06, 2006 3:05 PM
  •  Jim Perry wrote:
    I just mentioned the content manager issue as a possible mini-tutorial for the release party that the XNA team is having. Everyone did read about it, right?

    Cool, does that mean you're going ?

    I've been reading some of the blogs again and Shawn Hargreaves mentioned something about the ContentManager (http://blogs.msdn.com/shawnhar/archive/2006/09/06/743437.aspx). Looks like creating your own ContentManager in your own class isn't such a bad thing and at least least then you've only got to reference your game in the constructor which would make it easier for people to use other peoples classes.

    Terry

     

    Wednesday, December 06, 2006 6:47 PM
  •  Terry A. King wrote:

     Jim Perry wrote:
    I just mentioned the content manager issue as a possible mini-tutorial for the release party that the XNA team is having. Everyone did read about it, right?

    Cool, does that mean you're going ?

    I wish.   That is, unless MS sends me a plane ticket and books me a hotel room.  About the best I can hope for at this point is them putting out videos of the affair.

    Wednesday, December 06, 2006 7:27 PM
  • There's one problem with all of the solutions being discussed here (except for maybe Terry who seems to have caught on ;-) ).

    Content requirements can vary wildly between projects.  There is no one "right" way to manage your content.  Some games will be just fine with one content manager.  Others will need a complex streaming system where new content managers are instantiated as they move across the world (GTA style).  Others yet will choose to load shared elements (UI, common meshes, etc.) in one content manager, then all level specific stuff in another so you only have to unload a portion of your memory in between levels.

    So as you can see, none of these approaches you're all talking about are "right", they're just a tool in the toolbox.
    Wednesday, December 06, 2006 7:56 PM
  •  Joel Martinez wrote:
    There's one problem with all of the solutions being discussed here (except for maybe Terry who seems to have caught on ;-) ).

    Content requirements can vary wildly between projects.  There is no one "right" way to manage your content. 

    I knew this already.  Zoneless MMORPGS, any game with maps that don't use the same textures, etc. will require a custom content manager. For many (not all) Live Arcade style games, the basic content manager will probably work well.

    Wednesday, December 06, 2006 9:14 PM
  •  Joel Martinez wrote:
    So as you can see, none of these approaches you're all talking about are "right", they're just a tool in the toolbox.

    Indeed very true.

    Here's what I do: I have a ResourceManager game component (also exposed as a service) that has methods to create, manage and access ContentManagers.This way my game can use any number of, and even customized ContentManagers. Custom ContentManages might handle streaming and compressed data for example.

    So to acces content I always do that through the ResourseManager. Works very well for me.

    Thursday, December 07, 2006 12:59 PM