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
相关产品推荐
相关产品推荐

