C# MonoGame游戏菜单状态机空引用异常问题求助
MonoGame扑克游戏菜单切换时NullReferenceException问题修复
点击菜单按钮切换界面时程序崩溃,抛出异常:
System.NullReferenceException: 'Object reference not set to an instance of an object.' gameState was null.
错误指向mainMenu类中的gameState.CurrentGameState = GameStates.MainMenu;代码行。
问题代码片段
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(); } } } }
问题根源
mainMenu类中的gameState字段仅声明未赋值,导致点击按钮时访问null对象的属性触发异常。GameState.Game类中存在冗余的gameState字段,Initialize方法中还循环实例化新的Game对象,造成逻辑混乱。- 按钮事件中错误地将状态设为
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
相关产品推荐
相关产品推荐

