Monogame,Nez:切换场景,打开菜单

问题描述 投票:0回答:2

我从 Monogame 和 Nez 库/引擎开始,我正在努力打开菜单。我最初的想法是游戏将是一个场景类,另一个菜单。在 Esc 键上,我将 Core.scene 设置为菜单场景的新实例并记住原始场景。当菜单关闭时,我会将 Core.scene 设置回原来的。但是它不起作用;当一个场景被替换时,它就结束了,我无法恢复它。

所以我的问题是——我应该怎么做?显然我的设计有一些缺陷,但看不到正确的方法。打开菜单后我想暂停游戏,显示菜单(可能切换几个菜单屏幕),然后在菜单关闭时取消暂停游戏并继续玩。

c# monogame
2个回答
1
投票

没有看到你的代码很难确定,但听起来你需要的是实现某种状态机。您需要能够持有对非活动/背景状态(窗口、游戏区域等)的引用,以便在完成当前场景后它会恢复旧场景。

实现此目的的一种方法——也是我个人最喜欢的方法——是使用有限状态机 (FSM) 模式。

基本原理是有一堆对象(在这种情况下是场景),每次新场景开始时,您都将该场景添加到堆栈的顶部。该场景结束后,您将其从堆栈中删除(称为从堆栈中弹出)。

听起来很复杂,但实际上使用起来非常简单。这是一个简单的例子:

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.
    }
}

如您所见,通过此设置,StateManager 的(以及 Game1 的)Draw() 方法将始终主动渲染堆栈顶部的任何场景,这应该始终是使用 NewScene() 添加的最新场景方法。

RemoveScene() 方法非常简单,因为您基本上可以反转您在 NewScene() 中所做的事情。调用 sceneStack.Pop();和顶部场景被移除,将前一个场景设置为 currentScene 将确保堆栈中的最后一个场景将从它停止的地方开始。

当然,您需要为真正的游戏添加更多逻辑,因为在一个场景中完成的事情可能会改变堆栈下方场景的外观或行为,但其中大部分可以在 Update( ) 方法(与此处的 Draw() 方法基本相同)。

希望有帮助。


0
投票

这是我的第一个 Stack overflow 帖子。我创建这个帐户是为了回答这个问题。我知道这个问题已有 5 年历史,但我保证其他人正在寻找您发布的问题的答案。

不幸的是,我不能对 xRei 的回答投反对票。这不是一个糟糕的方法,但 xRei 的解决方案不适用于 Nez,并且很可能已经通过那个兔子洞抛弃了很多人。

快速总结一下为什么它不起作用,为什么你不能“复活场景”是因为当你在 Nez 中切换场景时,它会卸载所有实体。这些实体不再在内存中,因此当您尝试使用对先前场景的引用时,您会收到 Null Ref 错误。

然后您可以在内存中保留一个实体列表及其组件列表,然后将这些实体重新附加到您的场景中。这确实有效,请注意您必须重新创建相机。如果您真的想重新使用旧场景,是的,保留所有实体及其组件及其场景的引用确实有效。只需将其添加到 XRei 的答案中,然后创建一个新的场景相机。

一旦你这样做了,你可能会说? “这有点乱”。它确实变成了一团糟。

更好的想法是重新创建场景。如果您新建一个菜单场景,请重新创建它。如果您需要选项场景,请重新创建它。

你可能会问,我什么时候有两个场景都需要用到的数据? 好问题。我发现保持持久数据的最佳方法是使用“Nez.Persistence”,它包含在 Nez 中。它还允许您保存游戏并加载它。

如果你有任何问题,我可以回答更多。我知道让场景转换工作是一团糟,因为你必须使用“mgfxc”

© www.soinside.com 2019 - 2024. All rights reserved.