XNA Creators Club Online
Page 1 of 1 (5 items)
Sort Posts: Previous Next

NetworkGameStateManagement & DrawOrder

Last post 7/16/2009 7:33 PM by Allan Chaney. 4 replies.
  • 7/16/2009 6:34 PM

    NetworkGameStateManagement & DrawOrder

    I was wondering if anyone ran into this or could offer any insights.  I am developing a game using the NetworkGameStateManagement sample as a template.  On the menu screens, I would like to draw items from other Components that I've created.  For example, I'd like to draw either sprites or 3D models, and I have 2 Components called "SpriteManager" and "ModelManager" which are managing the key graphical elements for my game.

    The issue that I'm having is that within the ScreenManager, it seems logical that you'd want to paint a BackgroundScreen behind everything, and have the PauseMenuScreen and other MessageBoxScreens appear on top of everything (since they're overlays on top of the screen).

    Given that there is only a single DrawOrder property for ScreenManager, if I set ScreenManager.DrawOrder higher than my other components, the BackgroundScreen covers them so they can't be seen.  If I set ScreenManager.DrawOrder lower, then my sprites and models paint on top of the Pause and MessageBox screens.

    Any thoughts on this?  I was thinking I could break-out the Pause and MessageBox Screens into a separate Component so I can give them a higher DrawOrder than the other Screens, but that seems like a pretty invasive change, so I want to make sure there's not an easier way before I go down that path.

    Thanks!
  • 7/16/2009 6:52 PM In reply to

    Re: NetworkGameStateManagement & DrawOrder

    See here for one solution.
    XNA Framework Developer - blog - homepage
  • 7/16/2009 7:17 PM In reply to

    Re: NetworkGameStateManagement & DrawOrder

    If you use DrawableGameComponent, each drawing pass needs to be a separate component. Thus, the BackgroundScreen needs to be one component, and the PauseScreen needs to be another component.

    What I do is build my own renderer ("scene graph lite") which calls back to generate draw calls at the end of the frame. You could do something similar, and in your ScreenManager, you'd do something like:

      public void OnDraw(RenderManager renderer) {
        renderer.AddBackground(this.DrawBackground);
        renderer.AddForeground(this.DrawForeground);
      }
      void DrawBackground(GraphicsDevice gd, SpriteBatch sb) {
        ...
      }
      void DrawForeground(GraphicsDevice gd, SpriteBatch sb) {
        ...
      }


    When drawing your game content, you'd do something like:

      public void OnDraw(RenderManager renderer) { 
        renderer.AddOpaqueContent(this.DrawOpaque, this.WorldPosition); 
        renderer.AddTransparentContent(this.DrawTransparent, this.WorldPosition); 
      } 
      void DrawOpaque(GraphicsDevice gd, SpriteBatch sb) { 
        ... 
      } 
      void DrawTransparent(GraphicsDevice gd, SpriteBatch sb) { 
        ... 
      } 
     
     
    public delegate void RenderFunc(GraphicsDevice gd, SpriteBatch sb); 
     
    class RenderManager { 
      struct ToRender { 
        public ToRender(RenderFunc rf) { func = rf; } 
        public ToRender(RenderFunc rf, Vector3 p) { func = rf; pos = p; } 
        public RenderFunc func; 
        public Vector3 pos; 
      } 
      List<ToRender> Backgrounds = new List<ToRender>(); 
      List<ToRender> Opaque = new List<Opaque>(); 
      List<ToRender> Transparent = new List<Transparent>(); 
      List<ToRender> Foregrounds = new List<Foregrounds>(); 
      public void AddBackground(RenderFunc rf) { 
        Backgrounds.Add(new ToRender(rf)); 
      } 
      public void AddOpaque(RenderFunc rf, Vector3 pos) { 
        Opaque.Add(new ToRender(rf, pos)); 
      } 
      public void AddTransparent(RenderFunc rf, Vector3 pos) { 
        Transparent.Add(new ToRender(rf, pos)); 
      } 
      public void AddForeground(RenderFunc rf) { 
        Foregrounds.Add(new ToRender(rf)); 
      } 
     
      public override void Draw(GameTime gt) { 
        ... clear screen, start spritebatch, etc ... 
        Opaque.Sort(NearToFarComparer); 
        Transparent.Sort(FarToNearComparer); 
        foreach (ToRender tr in Backgrounds) tr.func(device, batch); 
        foreach (ToRender tr in Opaque) tr.func(device, batch); 
        foreach (ToRender tr in Transparent) tr.func(device, batch); 
        foreach (ToRender tr in Foregrounds) tr.func(device, batch); 
        Backgrounds.Clear(); 
        Opaque.Clear(); 
        Transparent.Clear(); 
        Foregrounds.Clear(); 
        ... end sprite batch ... 
      } 



    Use your imagination for how to glue this into the game framework. The renderer component has to render last of all components.
    Also, you may want to begin/end the sprite batch around each list traversal. (that'd make for a fine sub-function). And, when you start turning this into a scene graph proper, you'll end up abstracting "list + sort mode + ordering" into a "scene pass," which you could have more or less than 4 of. And you'll start having callbacks to prepare objects, telling them whether they are drawing into shadow maps, the backbuffer, or not at all this frame, etc. Scene graph design is a pretty deep subject ;-)

    Jon Watte, Direct3D MVP
    Tweets, occasionally
    kW X-port 3ds Max .X exporter
    kW Animation source code
  • 7/16/2009 7:23 PM In reply to

    Re: NetworkGameStateManagement & DrawOrder

    Thanks a bunch to you both!  I read Shawn's blog a couple weeks ago, but for some reason it didn't click until I just reread it.  jwatte, I follow what you're saying as well, and the fact that the BackgroundScreen and PauseScreen needed to be two separate Components is what threw me off initially.

    Thanks again for the help!   I think between both your inputs I can work out a solution.
  • 7/16/2009 7:33 PM In reply to

    Re: NetworkGameStateManagement & DrawOrder

    Yes setting draworders using the GameStateManagement sample can be a real mind screw.

    Here is what I did.

    First of all, I don't use backgroundscreen.cs for any drawing because as you noted, once you set the draworder for screenmanager.cs, all of the menuscreens are drawn at that draworder.  I think it's a little design flaw of the gamestatemanagement sample.  When they made screenmanager a component, I don't think they really considered that.  All of my background drawing is done in drawablegamecomponents that I create in the loadcontent method of screenmanager.cs.  That way I have access to them all from any screen through screenmanager.cs.  You need the access so you can turn them on and off.

    I set my screenmanager.cs draworder = 4 in it's constructor.

    public ScreenManager(Game game)  
                : base(game)  
            {  
                Camera = new Camera(Game);  
                //This sets the Draw Order for all information in Menu Screens that is drawn using the   
                //Draw methods contained within respective screen classes (since the screens draw methods are called from  
                //this ScreenManager DrawableGameComponent.  
                //Since I draw animations and particle effects using DrawableComponents, I can set their Draworder  
                //Seperately because they contain their own draw methods.  
                DrawOrder = 4;   
            } 
     
    Then in the loadcontent method of screenmanager.cs I add my components that will display in the background.
    I set their draworders right when I create/add them.  That keeps things clean for me.

    protected override void LoadContent()  
            {  
                // Load content belonging to the screen manager.  
                content = Game.Content;  
                spriteBatch = new SpriteBatch(GraphicsDevice);
                #region Game Services  
                //Game Services is a way for Game Components to communicate with each other since each component has a copy of Game  
                //SpriteBatch - Add to Game Services  
                Game.Services.AddService(typeof(SpriteBatch), spriteBatch);  
                //AudioManager  - Add to Game Services              
                audioManager = new AudioManager(Game, content);  
                Game.Components.Add(audioManager);  
                Game.Services.AddService(typeof(AudioManager), audioManager);
                #endregion  
     
                #region Make Menu Audio  
                TitleThemeCue = audioManager.GetCue("TitleTheme16Looped");
                
                #endregion  
     
                #region Game Components  
                fullScreenSmoke = new GenericParticleClass(Game, 20, "Particle Textures/MenuSmoke",  
                                                       40, 50, 0, 0, 1f, 3f, 3f, 7f, 1, 1, -.3f, .3f,  
                                                       SpriteBlendMode.AlphaBlend, false, 1, 0, 180, false, 0, 0);  
                Game.Components.Add(fullScreenSmoke);  
                fullScreenSmoke.DrawOrder = 1;
                #endregion  
     
                #region Make MenuClouds  
                menuClouds = new MenuClouds(Game);  
                menuClouds.DrawOrder = 2;  
                Game.Components.Add(menuClouds);
                #endregion  
     

    I have a smoke particle effect that is a drawablegamecomponent set at draworder = 1 and menuclouds that is just a regular drawablegamecomponent class set at draworder = 2.

    Now in the menuscreens, I can turn on and off these components any time I want with the enable and visible properties.  In order to do that you have to make sure you declare them as public in screenmanager.

    So at the top of screenmanager.cs

    public MenuClouds menuClouds;  
     
    //Declare Particle effects  
    public GenericParticleClass fullScreenSmoke;  
    Vector2 smokePos; 


    Once your components are made public in screenmanager and you have added/created them and set their draworder in screenmanager, you can access them from any screen that inherits from GameScreen because Gamescreen has that nifty ScreenManager property.  This of course includes GamePlayScreen.cs just in case you want to reuse some of those components for your game.  All of the game components that are only used in my game, I add those in GamePlaySCreen.cs but for anything used in the menu system, it gets added in screenmanager.cs. 

    So I have a title screen where I want clouds but no smoke so in the loadcontent method of that screen I do this:

    ScreenManager.fullScreenSmoke.Enabled = ScreenManager.fullScreenSmoke.Visible = false;  
    ScreenManager.menuClouds.Enabled = ScreenManager.menuClouds.Visible = true

    This turns the smoke off and turns the clouds on for that screen.  When I load the next screen, I can turn on or off other components.

    I should add that the smoke and clouds gets drawn behind the menutext because the menu text is drawn within the screens themselves which have a draw order of 4 as set in screenmanager.cs.

    This works well for me but it does get a little harry keeping track of everything.  Thus I comment a lot so I don't forget what I'm doing.

    Allan
Page 1 of 1 (5 items) Previous Next