You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MonoGame切换场景时生成无效窗口问题求助

MonoGame 场景切换避免多窗口问题解决方案

我是业余开发者,刚用MonoGame做游戏,现在遇到个麻烦:切换到MainMenu场景时,会弹出一个没用的新窗口,原来的Intro窗口还留在那失效了。以后要做几十个场景,这种情况绝对不行。我猜是调用Run()时创建了MainMenu的新实例导致的,试了些代码片段但没用,只想要切换场景时保持单个窗口/实例。现有scenes.cs、intro.cs、mainmenu.cs三个文件,代码结构如下。


问题根源

你的Scene类直接继承自Game,这就导致每个场景(Intro、MainMenu)都是独立的MonoGame游戏实例。调用scene.Run()时,MonoGame会为这个新的Game实例创建全新的窗口和渲染上下文,旧的Intro窗口因为调用了Exit()会停止更新但不会自动关闭,就出现了双窗口的问题。


解决方案

重构代码,让整个游戏只有一个Game核心实例,场景只是这个核心实例下的可切换逻辑模块,而非独立的Game对象。具体修改如下:

1. 重构Scene基类(不再继承Game)

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace MyGame
{
    // 场景基类,改为普通抽象类
    public abstract class Scene
    {
        protected GraphicsDevice _graphicsDevice;
        protected SpriteBatch _spriteBatch;
        protected ContentManager _content;
        protected Game _game;

        // 构造函数接收核心Game实例及必要资源
        public Scene(Game game, GraphicsDevice graphicsDevice, SpriteBatch spriteBatch, ContentManager content)
        {
            _game = game;
            _graphicsDevice = graphicsDevice;
            _spriteBatch = spriteBatch;
            _content = content;
        }

        // 每个场景必须实现的抽象方法
        public abstract void Initialize();
        public abstract void LoadContent();
        public abstract void Update(GameTime gameTime);
        public abstract void Draw(GameTime gameTime);
    }
}

2. 创建唯一的核心Game类

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MyGame
{
    public class GameCore : Game
    {
        private GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private Scene _currentScene;

        public GameCore()
        {
            _graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            IsMouseVisible = true;
            _graphics.IsFullScreen = true;
            _graphics.PreferredBackBufferWidth = 1366;
            _graphics.PreferredBackBufferHeight = 768;
            _graphics.PreferMultiSampling = false;
            _graphics.ApplyChanges();
        }

        protected override void Initialize()
        {
            // 启动时加载Intro场景
            SwitchScene(new Intro(this, GraphicsDevice, _spriteBatch, Content));
            base.Initialize();
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);
            _currentScene.LoadContent();
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            // 转发更新逻辑到当前场景
            _currentScene.Update(gameTime);
            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            // 转发绘制逻辑到当前场景
            _currentScene.Draw(gameTime);
            base.Draw(gameTime);
        }

        // 统一的场景切换方法
        public void SwitchScene(Scene newScene)
        {
            // 卸载当前场景资源
            Content.Unload();
            
            // 初始化并加载新场景
            _currentScene = newScene;
            _currentScene.Initialize();
            _currentScene.LoadContent();
        }
    }
}

3. 修改Intro场景

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MyGame
{
    public class Intro : Scene
    {
        // 你的原有逻辑变量
        private bool _fadingComplete;
        private bool _logoDisplayed;

        public Intro(Game game, GraphicsDevice graphicsDevice, SpriteBatch spriteBatch, ContentManager content)
            : base(game, graphicsDevice, spriteBatch, content)
        {
        }

        public override void Initialize()
        {
            // 你的初始化逻辑
            _fadingComplete = false;
            _logoDisplayed = false;
        }

        public override void LoadContent()
        {
            // 加载Intro所需资源
            // 示例:_logoTexture = _content.Load<Texture2D>("logo");
        }

        public override void Update(GameTime gameTime)
        {
            // 你的Logo显示/淡出逻辑
            if (_fadingComplete && _logoDisplayed)
            {
                // 调用核心Game的切换方法进入主菜单
                (_game as GameCore).SwitchScene(new MainMenu(_game, _graphicsDevice, _spriteBatch, _content));
            }
        }

        public override void Draw(GameTime gameTime)
        {
            _graphicsDevice.Clear(Color.Black);
            // 你的绘制逻辑
            // 示例:_spriteBatch.Begin(); _spriteBatch.Draw(_logoTexture, Vector2.Zero, Color.White); _spriteBatch.End();
        }
    }
}

4. 修改MainMenu场景

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace MyGame
{
    public class MainMenu : Scene
    {
        // 你的菜单逻辑变量
        // private Texture2D _menuButton;
        // private int _selectedOption;

        public MainMenu(Game game, GraphicsDevice graphicsDevice, SpriteBatch spriteBatch, ContentManager content)
            : base(game, graphicsDevice, spriteBatch, content)
        {
        }

        public override void Initialize()
        {
            // 你的菜单初始化逻辑
            // _selectedOption = 0;
        }

        public override void LoadContent()
        {
            // 加载菜单所需资源
            // _menuButton = _content.Load<Texture2D>("menu_button");
        }

        public override void Update(GameTime gameTime)
        {
            // 你的菜单更新逻辑(比如按键切换选项)
            var keyboard = Keyboard.GetState();
            if (keyboard.IsKeyDown(Keys.Down))
            {
                // _selectedOption = (_selectedOption + 1) % menuItemCount;
            }
        }

        public override void Draw(GameTime gameTime)
        {
            _graphicsDevice.Clear(Color.DarkBlue);
            // 你的菜单绘制逻辑
            // _spriteBatch.Begin(); 绘制菜单选项 _spriteBatch.End();
        }
    } 
}

效果说明

修改后,整个游戏只有GameCore这一个Game实例,所有场景都是它的逻辑模块。切换场景时不会创建新窗口,完全符合单窗口需求。后续新增场景只需继承Scene基类,实现对应抽象方法,再通过GameCore.SwitchScene()切换即可。

内容的提问来源于stack exchange,提问作者Florin Voicu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:25:58