This project is read-only.

GameStates are kept in memory

Nov 8, 2007 at 9:56 PM
This might be by design. By I think we should reconsider this.

If you do the following:
Start QuickStartEngine
- Main menu is now displayed
Launch the game
What would you expect should be kept in memory, both the main menu and the game state are in memory, I think this is an error any comments?
Nov 8, 2007 at 10:10 PM
This needs to be configurable in some way. The main menu -> game play transition may be a case where you want to just get rid of the main menu state. However, if you have a game play -> escape menu transition, chances are you will want to keep the game play state around while the escape menu is active, so you can draw the escape menu on top of the game play image and then easily go back to that state.
Nov 8, 2007 at 10:22 PM
We might keep the resources in memory, but I can't see the point of having the menus themselfe. Since the time delay we are talking here prob is less than 100ms (And that's a lot of time to create a menu) I would say that we should drop the states instead of keeping them in memory.

One issue here is actually performance, as you would need to do a few things to keep those items in mem. When leaving a menu you need to unhook all events, both for the menu but also for all of the controls on the menu. When re-entering the menu you would have to resetup the event handlers. All animation effect on the menu have to be reset, as you prob want them to restart when the menu is shown.

Removing the menu from the game and recreating it when invoked is in my view the better solution.
Nov 8, 2007 at 11:08 PM
That's why it should be controlled on a per-state basis, with the SuspendState() and ReEnterState() methods. The main menu can unload whatever resources that are appropriate when going to the game play state, then reload if the player goes back to the main menu. Obviously if you are in the main game playing state and press escape to bring up the escape menu to pause the game, you don't want the whole game play state to unload and have to reload the game when you exit the escape menu and go back to the game play state.
Nov 9, 2007 at 8:32 AM
My main consern here is that artifacts will be kept alive. As everything has a reference to Game, unless activily removed all types instantiated will be kept alive for the duration of the game, eventhough they aren't meant to. This also means that all eventing is alive and messages are sendt to "dead" instances. This might have unwanted, and hard to debug effects.
Nov 9, 2007 at 1:39 PM
So then how would you handle an in-game escape menu, like when the user presses the escape key and a small menu should pop up? The game world should still be rendered in the background, just in a paused state. We definitely don't want to dispose of the entire game play state while the little escape menu state is running.

Maybe we could implement two different state transition versions: one would be an absolute transition, replacing the current state, and the other would be a state stack push, to retain the current state.
Nov 9, 2007 at 3:31 PM
I could imagine that there would be a layered structure where the lowest layer was the game area where all 3d (terrain, entities, etc) on top of that there would be a series of "screens" screens would react similar to states, though they can either just show data, here thinking hud or aquire input lock, in case of menus.

The game in the bottom layer would be visible if the layer allows so, as these would support full alpha blending. They would be 2d (though able to show 3d elements)

Just some thoughts
Nov 9, 2007 at 4:06 PM
You're still left with the problem of unbinding the input from the game logic when you have menus. How would you implement these screens that can acquire input locks? It seems like the state containing the menu would have to be explicitly aware of how the screens should work, i.e.:

GamePlayState.Update()
{
    if(escapeMenu.Visible)
    {
        escapeMenu.Update()
    }
    else
    {
        // ... normal processing
    }
}
 
GamePlayState.Draw()
{
    // ... Normal render-queue setup
 
    if(escapeMenu.Visible)
        escapeMenu.Draw();
 
    graphics.Draw();  // Flush the render queue
}
 
GamePlayState.HandleInput(InputState state) // I know this isn't really how input will be handled, this is just an example.
{
    if(state.WasKeyPressed(Keys.Escape))
    {
        escapeMenu.Show();  // Loads menu and acquires input control
        Game.InputEvent -= HandleInput;  // Unregister the game play state input handler (this method)
        // This method will no longer be called until escapeMenu unacquires the input lock
    }
}
 
GamePlayState.EscapeMenuReturn()    // Event handler called when escape menu is done
{
    Game.InputEvent += HandleInput;  // Reacquire our own input lock
}


I can definitely see how HUD elements are part of the current game state and are draw as overlays, but it seems like anything that needs to acquire input or pause a game state should be in itself another game state. Let's say you start the game. The main menu state loads and starts processing. The user then selects "New Game." The main menu state will then pop itself from the global state stack, push the game loading state onto the stack, and then essentially delete itself. Then, after the game loading state finishes its job, it does the same thing: pops itself off the stack, pushes the loaded game play state onto the stack, then deletes itself. Now, the game play state is the only state still active and in memory (assuming the GC ran). While playing, the user now hits escape. The game play state will load up an instance of the escape menu state, and push it onto the stack, and remove its own input bindings. However, it does not remove itself from the stack. Now, each frame the game play state will be processed, followed by the escape menu state, so we have proper rendering order but game play cannot process input. Now, when the escape menu closes, it just pops itself off the stack and deletes itself. The game play state now is the top of the stack, so it can reacquire the input bindings. Also, the escape menu could pop off two states from the stack (itself and the game play state), then push on a new main menu state instance if the user hits "Back to Main Menu."

The states aren't currently implemented like this, but the state system interface can easily handle this with the EnterState/ExitState/SuspendState/ReEnterState calls.
Nov 9, 2007 at 7:03 PM
I don't know how it actually would work, but could imagine something like this
ScreenManager.Sort("Visibility,DrawOrder", Descending); // Also UpdateOrder could have been used
foreach (Screen screen in ScreenManager.Children)
{
    if (screen.Visible == false)
    {
        break;
    }
 
    // This is only placed inside update not draw
    if (screen.Enabled == false)
    {
        continue;
    }
 
    screen.Update(gameTime); // This might as well be screen.Draw(gameTime);
}
Then each screen would have a Initialize and Dispose (and Update/Draw). In this case the ingame menu is shown
protected override  void InitializeCore()
{
    this.Game.Input.Lock(this); // Locks all input into this screen, which puts this on the top of the lock stack
 
    // There has to be a mechanism to get the MainScreen which contains the actual game models/terrain
    this.Game.MainScreen.Enabled = false;
}
 
protected override void Dispose(bool disposing)
{
    this.Childrens.Dispose();
 
    if (disposing == true)
    {
        this.Game.Input.Release(this); // pops the top of the lock stack
        this.Game.MainScreen.Enabled = true;
    }
}
Or at least something like this. Also since all Entities bacially need to listen to EngineMessage I would suggest moving that to the base Entity creating something like this:
Entity.Initialize(QSGame game)
{
    this.game = game;
    this.game.EngineMessage += this.EngineMessage;
}
 
bool Entity.enabled = true;
Entity.Enabled
{
    get {return this.enabled;}
    set 
    {
        if (this.enabled == value)
        {
            return;
        }
 
        this.enabled = value;
        if (this.enabled == true)
        {
            this.Game.EngineMessage += this.EngineMessage;
        }
        else
        {
            this.Game.EngineMessage -= this.EngineMessage;
        }
    }
}
 
Entity.EngineMessage(args)
{
    this.EngineMessageCore(args);
}
 
protected virtual Entity.EngineMessageCore(args)
{
}

This way anyone can just overload EnginrMessageCore and handle the messages, no need to think about hook/unhook
Nov 9, 2007 at 7:09 PM
So would each state have a screen manager, or would this replace the state manager all-together?
Nov 9, 2007 at 7:56 PM
SceneManager=StateManager sorry about mixing terms also Scene=State
Nov 9, 2007 at 8:25 PM
Wait, how does the scene manager play into all this? You want the screens to be a part of the scene manager?
Nov 9, 2007 at 8:40 PM
Yes, or rather the ScreenManager is an node on the SceneGraph, and as such Screen are just Entities
Nov 9, 2007 at 9:32 PM
How do the other menus fit into this then? Like the main menu? Chances are you're not going to have a scene manager setup when you're at the main menu.
Nov 10, 2007 at 12:43 AM
In my mind everything that exists, exists in the graph. Why would you have something which is not part of the graph?
Nov 10, 2007 at 1:38 AM
I was thinking of the scene manager as the manager for everything in the game world, a step under a scene graph. We can extend it to encompass 2D overlays and GUI elements as well, but it just seems weird saying, "Give me a quad-tree scene manager so I can draw a menu."
Nov 10, 2007 at 12:19 PM
Edited Nov 10, 2007 at 12:20 PM
I think that were I'm confused, why does the SceneGraph use QuadTree? isn't it the implementation in the nodes of the Graph? Like
Root - SceneGraph
    Terrain - BSP Structure
    Entities - QuadTree
    Screens - List<Screen>
Any node can use any implementation it want, so when loading another map Terrain would simply be swapped to a QuadTree as well. Wouldn't that be a better solution?
Nov 10, 2007 at 5:38 PM
That's a possibility, though we're probably going to need to make them mutually exclusive. i.e., if you're using a quad-tree, you can't just put a BSP tree into a quad-tree node, and vice versa; at least not while expecting good performance as you'd be limiting the usefulness of the algorithms. Imagine a quad-tree root node being placed in a BSP leaf. The space that the leaf occupies is arbitrary, it doesn't even need to be rectangular. How would a quad-tree partition that space efficiently? Also, for efficiency, the worlds are going to be baked into whatever format we need at build-time.

We could do something like:

public class SceneGraph   // Your definition of scene graph
{
    private ISceneManager sceneManager;  // Handles 3D spatial partitioning and management... can be null for main menu, etc.
    private SomeScreenContainer screens;
}

That would be sort of like the best of both worlds. You have a global scene graph component, and still maintain separation between the scene managers.

Nov 11, 2007 at 8:43 AM
What does sceneManager contain?
public class SceneGraph   // Your definition of scene graph
{
    private Terrain terrain;  // Terrain spatial partitioned in any format Quad/BSP
    private SomeScreenContainer screens;
    private SomeEntityContainer dynamics; // Sorted for optimum rendering
    private SomeEntityContainer statics; //Whatever sorting makes sense
}
This is more my thinking
Nov 11, 2007 at 3:26 PM
So every map must be a terrain? What if we want to support a Constructive Solid Geometry approach?

Basically, sceneManager would contain an instance of a class that controls level geometry and entities. If you want to maintain a separate list of entities, that's fine, but the scene manager should be in charge of placing them in the world and making sure they're rendered when visible. A single walk of the scene manager will build the entire render queue, as the entites (both dynamic and static) will be in the leaves of whatever spatial partitioning is used.
Nov 11, 2007 at 7:13 PM
We should allow for exteriors, terrain or geometry. Geometry would be similar to the billboard sample on the XNA site.
Nov 11, 2007 at 7:35 PM
So we're not going to support interiors?
Nov 11, 2007 at 8:01 PM
There is nothing preventing CSG using this model, the ScreenGraph is totally terrain agnostig, it only renders/updates it. The implementation of Terrain can be anything you want, even streaming live data from satelite if you ever wanted that.

No it's not required that there is a terrain. And using terrain as the node type might not be the right solution, maybe using a TerrainManager is better, which might allow you to load muliple terrains at the same time, this way you could partition the terrain (How that you actually work I have no clue).

The SceneGraph isn't resposible for anything else but to invoke Update and maybe Render on the collections, it's the task of each collection to determine if a node should be rendered or not. So you would have something like this for the Update loop in QuickStartEngine:
        protected override void Update(GameTime gameTime)
        {
            ...
            this.SceneGraph.Update(gameTime);
            ...
        }
And for Draw you would have something like this in QuickStartGame:
        protected override void Draw(GameTime gameTime)
        {
            ...
            this.SceneGraph.Draw(gameTime);
            ...
        }
Then SceneGraph would be responsible for updating the nodes:
        public void Update(GameTime gameTime)
        {
            // Must be updated first
            this.Cameras.Update(gameTime);
 
            this.TerrainManager.Update(gameTime);
            this.Entities.Update(gameTime);
            this.Statics.Update(gameTime);
        }
And Drawing them:
        public void Draw(GameTime gameTime)
        {
            this.TerrainManager.Draw(gameTime);
            this.Entities.Draw(gameTime);
            this.Statics.Draw(gameTime);
        }
Notice that the Cameras which are part of the graph aren't drawn, there is no need to, but all other entities are using the currently active camera to get projection/world etc.

Each collection is then responsible for drawing/updating depending on the sorting of the entities, i.e. sorted by Visible and Enabled, would then make the Update/Draw look like;
        // Sorted by Enabled Descending (Those who are enabled are first)
        public void Update(GameTime gameTime)
        {
            for (int i = this.Count - 1; i >= 0; i--)
            {
                if (this[i].Enabled == false)
                {
                    break;
                }
 
                this[i].Update(gameTime);
            }
        }
And Drawing:
        // Sorted by Visible Descending (Those who are visible are first)
        public void Draw(GameTime gameTime)
        {
            for (int i = this.Count - 1; i >= 0; i--)
            {
                if (this[i].Visible == false)
                {
                    break;
                }
 
                this[i].Draw(gameTime);
            }
        }
Now how the sort is implemented and when to invoke sorting is the golden egg here. I'm not too conserned as different algoritmns can be tested easily simply be replacing the type used.

Comments?
Nov 11, 2007 at 8:45 PM


shawmishrak wrote:
So we're not going to support interiors?


I though we'd discussed using something like a quake bsp once we have physics for it.
Nov 12, 2007 at 12:45 AM
Okay, so it looks like we're kind of talking about the same thing, I'm just calling it a scene manager and you're calling it Terrain. I was getting confused because I figured you meant that the actual height-map terrain implementation should be at the root of the scene graph.

I'm still not buying why we need to seperate the entities from the scene manager (your Terrain). All spatial partitioning should happen in the scene manager, as it has the best understanding of how the world is organized, especially when you start getting into BSP/portal rendering. The best you can do otherwise is a frustum-check, and if you let the scene manager set the Visible property, then there's really no reason to run a second loop outside of the spatial partitioning just to call Draw() on the entities.

LordIkon, do you have any plans of moving the heightmap -> geometry conversion from run-time to compile-time via a content processor?
Nov 12, 2007 at 1:56 AM
So how would SceneManager then look like? Wouldn't it need to contain references to all entities then? If so what's then the difference between SceneManager and SceneGraph?
Nov 12, 2007 at 4:30 AM
The separation came from the discussion of where to put the Screen objects. If you want to put them in the scene graph, and every menu must be implemented as a Screen, then it seems like this separation should exist. Otherwise, you're left with having to instantiate some sort of scene manager (which implies some sort of spatial partitioning algorithm) in order to draw the main menu.

The main difference is that a scene manager is an implementation of a spatial partitioning algorithm that maintains static world geometry and all contained entities, where as the scene graph maintains the scene manager, and all other elements that are not part of the physical world. The entities should be a part of the scene manager, I don't see why they need to be a part of the "Scene Graph."

Honestly, I think having the names Scene Graph and Scene Manager is a bit confusing, as they should really mean the same thing, and in this case they don't. Let's say we have SceneManager and LayoutManager. SceneManager handles everything related to the physical world, and LayoutManager handles all 2D overlays (including GUI elements). Is there any reason that these need to be one in the same? They have different purposes, the only commonality is the fact that they both interact with the renderer. Roughly, it'd be:

public class LayoutManager
{
    private GUIElementCollection elements;   // GUIElement (or similar name) base class contained in GUI namespace.
 
    public void InsertGUIElement(...);
    public void RemoveGUIElement(...);
 
    public void Update(...);
    public void Draw(...);
}
 
public interface ISceneManager    // Scene manager interface
{
    public void InsertEntity(...);
    public void RemoveEntity(...);
 
    public void Update(...);
    public void Draw(...);
}
 
Then, in QuickStartGame  (or in GameState, depending on if we keep that system):
 
public void Update(...)
{
    if(sceneManager != null)    // We may not always have a screen manager set up, especially for out-of-game menus
        sceneManager.Update(...);
 
    layoutManager.Update(...);
}
 
public void Draw(...)
{
    if(sceneManager != null)
        sceneManager.Draw(...);
 
    graphics.RenderFrame();  //  Process 3D render queue
 
    layoutManager.Draw(...);
 
    graphics.RenderOverlay();  // Process 2D render queue

Now, whether you keep the state system and each state gets a layout manager, or drop the state system and the layout manager can contain multiple different layouts, is just a matter of implementation.
Nov 12, 2007 at 4:37 AM
I'm not sure how I could make calculations (which are in code) to make a heightmap during compile time. It may be possible to read the heightmap image/geometry and create a file that contains the height data, then the file could be read it at runtime. However the benefit to that would be small I believe. My guess is that around 95% of the load time is in the following: Smoothing the terrain, setting up a quad-tree, and setting up multiple LODs for each section of the quad-tree. I'm not sure this stuff could be put into a file or not. If it could, it could be a large file. I'd have to have a file that included each vertex and index in each quad-tree section, and every patch (LOD) it contained, etc....

Maybe I'm just overthinking this, what did you have in mind?
Nov 12, 2007 at 5:19 AM
Also, I've started an 'issue' for the scene manager to be created. I've attached a document to it. I'm hoping we can draw out, or UML a full design for the scene manager and its relation to the terrain/quadtree/terrainpatch system in place, and determine what changes are needed.

http://www.codeplex.com/QuickStartEngine/WorkItem/View.aspx?WorkItemId=4378
Nov 12, 2007 at 10:02 AM
I also create a discussion(Scene Manager Design) for it as Issues do not allow formatting