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

MonoGame游戏状态切换报错求助:新手实现状态切换遇问题

MonoGame高效游戏状态切换方案及问题修复

我是MonoGame新手,正在寻找高效的游戏状态切换方法,比如玩家死亡时切换至菜单界面,点击按钮时切换至游戏界面。我尝试了如下代码实现,但在Update方法中将状态设置为'game'时出现错误。

原代码:

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

namespace Jetpack;

public class Game1 : Game
{
    private GraphicsDeviceManager _graphics;
    GameScreen gameScreen = new GameScreen();
    MenuScreen menuScreen = new MenuScreen();
    string state = "menu";

    public Game1()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
    }

    protected override void Initialize()
    {   
        if(state == "game")
        {
            gameScreen.Initialize();
        }

        if(state == "menu")
        {
            menuScreen.Initialize();
        }

        base.Initialize();
    }

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

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

        if(state == "game")
        {
            gameScreen.Update();
        }

        if(state == "menu")
        {
            menuScreen.Update();
            if(Globals.kState.IsKeyDown(Keys.A))
            {
                state = "game";
            }
        }
        

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {   
        Globals._spriteBatch.Begin();
        
        if(state == "game")
        {
            gameScreen.Draw();
        }

        if(state == "menu")
        {
            menuScreen.Draw();
        }

        Globals._spriteBatch.End();

        base.Draw(gameTime);
    }
}

问题分析

  1. 初始化逻辑缺失:Initialize方法仅在游戏启动时执行一次,启动时状态是menu,gameScreen从未被初始化。当切换到game状态时,调用gameScreen.Update()会因为未初始化而出错。
  2. 字符串状态不安全:用字符串表示状态容易出现拼写错误,且没有类型校验。
  3. SpriteBatch未声明:Game1类中没有定义_spriteBatch字段,编译时会报错。

修复方案

1. 使用枚举定义游戏状态

替换字符串状态为枚举,提升类型安全性:

public enum GameState
{
    Menu,
    Game
}

2. 重构Game1类,完善状态切换逻辑

确保状态切换时执行对应屏幕的初始化,同时修复字段缺失问题:

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

namespace Jetpack;

public enum GameState
{
    Menu,
    Game
}

public class Game1 : Game
{
    private GraphicsDeviceManager _graphics;
    private SpriteBatch _spriteBatch; // 补充缺失的字段
    private GameScreen _gameScreen;
    private MenuScreen _menuScreen;
    private GameState _currentState = GameState.Menu;

    public Game1()
    {
        _graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        IsMouseVisible = true;
        // 延迟初始化屏幕,避免提前依赖GraphicsDevice
        _gameScreen = new GameScreen();
        _menuScreen = new MenuScreen();
    }

    protected override void Initialize()
    {
        // 初始化当前状态对应的屏幕
        SwitchScreen(_currentState);
        base.Initialize();
    }

    protected override void LoadContent()
    {
        _spriteBatch = new SpriteBatch(GraphicsDevice);
        Globals._spriteBatch = _spriteBatch; // 同步到全局变量
        // 可以在这里让屏幕加载自己的资源
        _gameScreen.LoadContent(Content);
        _menuScreen.LoadContent(Content);
    }

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

        // 处理状态切换
        switch (_currentState)
        {
            case GameState.Game:
                _gameScreen.Update(gameTime);
                // 示例:玩家死亡时切换回菜单
                if (_gameScreen.IsPlayerDead)
                {
                    SwitchScreen(GameState.Menu);
                }
                break;
            case GameState.Menu:
                _menuScreen.Update(gameTime);
                if (Globals.kState.IsKeyDown(Keys.A))
                {
                    SwitchScreen(GameState.Game);
                }
                break;
        }

        base.Update(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.Black); // 先清空屏幕,避免画面残留
        _spriteBatch.Begin();
        
        switch (_currentState)
        {
            case GameState.Game:
                _gameScreen.Draw(_spriteBatch);
                break;
            case GameState.Menu:
                _menuScreen.Draw(_spriteBatch);
                break;
        }

        _spriteBatch.End();
        base.Draw(gameTime);
    }

    // 封装状态切换逻辑,统一处理屏幕初始化/重置
    private void SwitchScreen(GameState newState)
    {
        _currentState = newState;
        switch (newState)
        {
            case GameState.Game:
                _gameScreen.Initialize(); // 切换到游戏时重新初始化,重置游戏状态
                break;
            case GameState.Menu:
                _menuScreen.Initialize();
                break;
        }
    }
}

3. 完善屏幕类的基础结构

确保GameScreen和MenuScreen包含必要的方法:

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

public class GameScreen
{
    public bool IsPlayerDead { get; private set; }

    public void Initialize()
    {
        IsPlayerDead = false;
        // 初始化游戏元素:玩家位置、分数等
    }

    public void LoadContent(ContentManager content)
    {
        // 加载游戏资源:纹理、音效等
    }

    public void Update(GameTime gameTime)
    {
        // 游戏逻辑更新:玩家移动、碰撞检测等
        // 示例:模拟玩家死亡
        if (Globals.kState.IsKeyDown(Keys.D))
        {
            IsPlayerDead = true;
        }
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        // 绘制游戏元素
    }
}

public class MenuScreen
{
    public void Initialize()
    {
        // 初始化菜单元素:按钮位置、文本等
    }

    public void LoadContent(ContentManager content)
    {
        // 加载菜单资源:字体、按钮纹理等
    }

    public void Update(GameTime gameTime)
    {
        // 菜单逻辑更新:按钮点击检测等
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        // 绘制菜单
    }
}

// 全局辅助类
public static class Globals
{
    public static SpriteBatch _spriteBatch;
    public static KeyboardState kState;
}

关键优化点

  • 类型安全的状态管理:枚举替代字符串,避免拼写错误,代码可读性更强。
  • 统一的状态切换流程:用SwitchScreen方法封装切换逻辑,确保切换时执行屏幕的初始化/重置,避免未初始化的问题。
  • 资源加载分离:每个屏幕负责自己的资源加载,逻辑更清晰。
  • 画面清理:Draw方法中先清空屏幕,避免不同状态的画面残留。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:32:09