4

I am starting with Monogame and Nez library/engine, and I am struggling with opening menu. My original idea was that game will be one Scene class, menu another one. On Esc key I will set Core.scene to new instance of menu scene and remember the original scene. When menu closes, I'll set Core.scene back to original one. However it does not work; when a scene is replaced, it is ended, and I cannot ressurect it.

So my question is - how should I do it? Clearly I have some flaw in my design, but can't see the right way to do it. Upon opening menu I want to pause game, show menu (possibly switch several menu screens), then unpause the game when menu closes and continue playing.

Kaven
  • 147
  • 1
  • 11

2 Answers2

1

It's hard to be certain without seeing your code, but it sounds like what you need is to implement a state machine of some kind. You need to be able to hold a reference to the inactive/background state (a window, the play area, etc), so that once you are done with the current scene it resumes the old one.

One way to do this--and the one I personally like best--is to use a finite state machine(FSM) pattern.

The basic principle is that there is a stack of objects(scenes in this case), and each time a new scene begins you add that scene on the top of the stack. Once that scene concludes, you remove it from the stack (called popping from the stack).

It sounds convoluted but it's actually quite simple to use. Here's a simple example:

public class StateManager
{
    private ContentManager Content { get; set; }
    private Game1 Game { get; set; }
    private Stack<Scene> sceneStack = new Stack<Scene>();
    private GameState currentScene;
    private GameState previousScene;

    public StateManager(ContentManager content, Game1 game)
    {
        // Pretty common to pass one or both of these instances around in your code.
        this.Game = game;
        this.Content = content;
    }

    public void AddScene(GameState newScene)
    {
        // This is the crucial part. We are saving the state of the old scene.

        if (currentScene != null)
            this.previousScene = currentScene;

        sceneStack.Push(newScene); // New scene is stacked 'on top' of the old one
        this.currentScene = newScene;
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        // Out in Game1.cs we are calling this method from within
        // its own Draw() method.

        this.currentScene.Draw(spriteBatch);
    }
}

public class Scene : GameState
{
    // Any state information needed for your scene like menus, terrain, etc
}

public abstract class GameState
{
    // The base class for your Scenes which contains code relevant to all scene types.
    // You could go a step further and make an interface as well.

    public virtual void Draw(SpriteBatch spriteBatch)
    {
        // Logic to draw the game state. Textures, sprites, text, child
        // elements contained inside this state, etc.
    }
}

As you can see, with this set up, the StateManager's(and thus Game1's) Draw() method will always be actively rendering whatever scene is on the top of the stack, which should always be the newest scene added with the NewScene() method.

The RemoveScene() method is simple enough as you basically reverse what you did in NewScene(). Call sceneStack.Pop(); and and top scene is removed, setting the previous scene as currentScene will then ensure the last scene on the stack will pick up where it left off.

Of course there is a lot more logic you'll need to add for a real game, as things done in one scene might change the look or behavior of scenes further down the stack, but most of that can be handled within the Update() methods(which work fundamentally the same as the Draw() method here).

Hope that helps.

xRei
  • 11
  • 1
1

xRei's answer is not a bad approach but xRei's solution does not work with Nez, and likely have thrown off many people by going down that rabbit hole.

To quickly sum up why it doesn't work, and why you can't "resurrect a scene" is because when you switch scenes in Nez, it unloads all entities. These entities are no longer in memory, so when you attempt to use an reference to a previous scene, you get an Null Ref error.

You COULD then keep a list of Entities and a list of their components in memory, then re-attach those entities to you scene. This actually does work, just be aware that you Have to recreate the camera. If you REALLY want to re-use old scenes, yes keeping a reference of all Entities and their components with their Scene does work. Just add that to XRei's answer, and create a new Scene Camera.

Once you do that, you might say? "This is kind of a mess". Which it does turn to a mess.

A better idea, is to just recreate the scenes anew. If you new a menu scene, create it anew. If you need an options scene, create it anew.

You may ask, when if I have data that needs to be used at both scenes? Great Question. I found the best way to keep persistent data, is to use "Nez.Persistence" It's included with Nez. It also allows you to save the game and load it.

I know getting the Scene Transitions to work is a mess, since you have to use "mgfxc"

E_net4
  • 27,810
  • 13
  • 101
  • 139
USB-Port
  • 11
  • 1