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

C# MonoGame游戏菜单状态机空引用异常问题求助

MonoGame扑克游戏菜单切换时NullReferenceException问题修复

点击菜单按钮切换界面时程序崩溃,抛出异常:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

gameState was null.

错误指向mainMenu类中的gameState.CurrentGameState = GameStates.MainMenu;代码行。

问题代码片段

namespace PokerGameNEA.UI
{
    partial class mainMenu: Panel
    {
        private GameState.Game gameState;
        public void BuildUI()
        {
            var label1 = new Label();
            label1.Text = "POKER";
            label1.Font = MyraEnvironment.DefaultAssetManager.Load<SpriteFontBase>("IyPZIZAmLtaftSwqk3gBCKNd.ttf.fnt");
            label1.TextAlign = FontStashSharp.RichText.TextHorizontalAlignment.Center;
            label1.HorizontalAlignment = Myra.Graphics2D.UI.HorizontalAlignment.Center;

            _playButton = new MenuItem();
            _playButton.Text = "PLAY";
            _playButton.Id = "_playButton";
            _playButton.Selected += (s, a) =>
            {
                gameState.CurrentGameState = GameStates.MainMenu;
            };
        }
    }
}

GameState状态机代码

namespace PokerGameNEA.States
{
    public class GameState
    {
        public enum GameStates
        {
            MainMenu,
            HowToPlay,
            EndOfGame,
        }
        public class Game
        {
            public Desktop _desktop;
            private mainMenu mainMenu;
            private howToPlay howToPlay;
            private Game gameState;

            public void Initialize()
            {
                mainMenu = new mainMenu();
                howToPlay = new howToPlay();
                _desktop = new Desktop();
                _desktop.Root = mainMenu;

                gameState = new GameState.Game();
            }

            public GameStates CurrentGameState { get; set; }

            public Game()
            {
                CurrentGameState = GameStates.MainMenu;
            }

            public void PerformActionsBasedOnState()
            {
                switch (CurrentGameState)
                {
                    case GameStates.MainMenu:
                        _desktop.Root = mainMenu;
                        break;

                    case GameStates.HowToPlay:
                        _desktop.Root = howToPlay;
                        break;

                    case GameStates.EndOfGame:
                        _desktop.Root = howToPlay;
                        break;

                    default:
                        Environment.Exit(0);
                        break;
                }
            }

            public void Draw(GameTime gameTime)
            {
                _desktop.Render();
            }
        }
    }
}

问题根源

  1. mainMenu类中的gameState字段仅声明未赋值,导致点击按钮时访问null对象的属性触发异常。
  2. GameState.Game类中存在冗余的gameState字段,Initialize方法中还循环实例化新的Game对象,造成逻辑混乱。
  3. 按钮事件中错误地将状态设为MainMenu,和当前状态重复,未实现切换逻辑。

修复方案

1. 给mainMenu类添加构造函数传递Game实例

修改mainMenu类,通过构造函数接收GameState.Game实例,确保gameState字段被正确赋值:

namespace PokerGameNEA.UI
{
    partial class mainMenu: Panel
    {
        private GameState.Game gameState;

        // 添加构造函数接收Game实例
        public mainMenu(GameState.Game gameInstance)
        {
            gameState = gameInstance;
            BuildUI();
        }

        public void BuildUI()
        {
            // 原有UI构建代码...

            _playButton.Selected += (s, a) =>
            {
                // 修正为切换到目标状态,比如HowToPlay
                gameState.CurrentGameState = GameState.GameStates.HowToPlay;
            };
        }
    }
}

2. 修复GameState.Game类的逻辑错误

移除冗余字段,创建菜单时传入当前Game实例:

namespace PokerGameNEA.States
{
    public class GameState
    {
        public enum GameStates
        {
            MainMenu,
            HowToPlay,
            EndOfGame,
        }
        public class Game
        {
            public Desktop _desktop;
            private mainMenu mainMenu;
            private howToPlay howToPlay;
            // 移除冗余的gameState字段

            public void Initialize()
            {
                // 创建菜单时传入当前Game实例
                mainMenu = new mainMenu(this);
                howToPlay = new howToPlay(this);
                _desktop = new Desktop();
                _desktop.Root = mainMenu;

                // 移除循环实例化Game的错误代码
            }

            public GameStates CurrentGameState { get; set; }

            public Game()
            {
                CurrentGameState = GameStates.MainMenu;
            }

            public void PerformActionsBasedOnState()
            {
                switch (CurrentGameState)
                {
                    case GameStates.MainMenu:
                        _desktop.Root = mainMenu;
                        break;
                    case GameStates.HowToPlay:
                        _desktop.Root = howToPlay;
                        break;
                    case GameStates.EndOfGame:
                        // 替换为对应的EndOfGame菜单实例
                        // _desktop.Root = endOfGameMenu;
                        break;
                    default:
                        Environment.Exit(0);
                        break;
                }
            }

            public void Draw(GameTime gameTime)
            {
                _desktop.Render();
            }
        }
    }
}

3. 同步修改howToPlay类

给howToPlay类添加相同的构造函数,避免后续切换时出现同样的null问题:

namespace PokerGameNEA.UI
{
    partial class howToPlay: Panel
    {
        private GameState.Game gameState;

        public howToPlay(GameState.Game gameInstance)
        {
            gameState = gameInstance;
            // 构建howToPlay界面的代码...
        }
    }
}

4. 在游戏主循环中触发状态更新

在MonoGame的Update方法中调用PerformActionsBasedOnState,确保状态变化后UI及时更新:

protected override void Update(GameTime gameTime)
{
    // 其他更新逻辑...
    _gameState.PerformActionsBasedOnState();
    base.Update(gameTime);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 12:01:33