本节课目标
-
按 ESC 或 P 键暂停游戏
-
暂停时画面变暗,显示菜单选项
-
选项:继续游戏、返回标题、退出
-
暂停时所有敌人和子弹停止移动
第一步:创建暂停菜单类
右键项目 → 添加 → 类 ,文件名 PauseMenu.cs:
cs
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using FontStashSharp;
using System;
using System.Collections.Generic;
namespace MY_FIRST_GAME
{
public class PauseMenu
{
private List<string> options;
private int selectedIndex;
private KeyboardState previousKeyboard;
private KeyboardState currentKeyboard;
public PauseMenu()
{
options = new List<string>
{
"继续游戏",
"返回标题",
"退出游戏"
};
selectedIndex = 0;
}
// 返回值:0=继续, 1=返回标题, 2=退出, -1=还在菜单中
public int Update()
{
previousKeyboard = currentKeyboard;
currentKeyboard = Keyboard.GetState();
if (IsKeyJustPressed(Keys.W) || IsKeyJustPressed(Keys.Up))
{
selectedIndex--;
if (selectedIndex < 0) selectedIndex = options.Count - 1;
}
if (IsKeyJustPressed(Keys.S) || IsKeyJustPressed(Keys.Down))
{
selectedIndex++;
if (selectedIndex >= options.Count) selectedIndex = 0;
}
if (IsKeyJustPressed(Keys.Enter) || IsKeyJustPressed(Keys.Space))
{
return selectedIndex;
}
return -1;
}
private bool IsKeyJustPressed(Keys key)
{
return currentKeyboard.IsKeyDown(key) && previousKeyboard.IsKeyUp(key);
}
public void Draw(SpriteBatch spriteBatch, SpriteFontBase font, GraphicsDevice device)
{
// 半透明黑色背景
Texture2D overlay = new Texture2D(device, 1, 1);
overlay.SetData(new[] { new Color(0, 0, 0, 180) });
spriteBatch.Draw(overlay, new Rectangle(0, 0, 800, 600), Color.White);
// 标题
string title = "- 暂停 -";
Vector2 titleSize = font.MeasureString(title);
spriteBatch.DrawString(font, title,
new Vector2(400 - titleSize.X / 2, 150), Color.White);
// 选项
float startY = 250;
for (int i = 0; i < options.Count; i++)
{
Color color = (i == selectedIndex) ? Color.Gold : Color.LightGray;
string text = (i == selectedIndex) ? $"> {options[i]} <" : $" {options[i]} ";
Vector2 textSize = font.MeasureString(text);
spriteBatch.DrawString(font, text,
new Vector2(400 - textSize.X / 2, startY + i * 50), color);
}
// 操作提示
string hint = "↑↓ 选择 | 回车 确认 | ESC 继续";
spriteBatch.DrawString(font, hint,
new Vector2(400 - font.MeasureString(hint).X / 2, 450), Color.Gray);
}
public void Reset()
{
selectedIndex = 0;
}
}
}
第二步:改造 Game1.cs
- 添加字段:
csharp
private PauseMenu pauseMenu = default!;
private bool isPaused = false;
- 在 Initialize 里创建暂停菜单:
csharp
pauseMenu = new PauseMenu();
- 在 Update 开头处理暂停键:
在所有 switch 之前加:
csharp
// ★ 暂停切换
if (keyboard.IsKeyDown(Keys.P) && !isPaused ||
keyboard.IsKeyDown(Keys.Escape) && sceneManager.CurrentScene == SceneType.Game)
{
isPaused = !isPaused;
pauseMenu.Reset();
}
- 把 Game 状态的更新包裹在暂停检查里:
csharp
case SceneType.Game:
if (!isPaused)
{
// 原有的所有 Game 更新逻辑
}
break;
也就是把 case SceneType.Game: 里面那堆代码外面包一层 if (!isPaused)。
- 在 Draw 里画暂停菜单:
在 DrawGameWorld 和 DrawGameUI 之后,加:
csharp
// ★ 画暂停菜单
if (isPaused && sceneManager.CurrentScene == SceneType.Game)
{
_spriteBatch.Begin();
int result = pauseMenu.Update();
pauseMenu.Draw(_spriteBatch, font, GraphicsDevice);
_spriteBatch.End();
if (result == 0) // 继续游戏
{
isPaused = false;
}
else if (result == 1) // 返回标题
{
isPaused = false;
sceneManager.CurrentScene = SceneType.Title;
saveData.TotalCoinsCollected += coinsCollectedThisGame;
saveData.TotalEnemiesDefeated += enemiesDefeatedThisGame;
SaveManager.Save(saveData);
}
else if (result == 2) // 退出
{
Exit();
}
}
注意:暂停菜单需要独立的 Begin/End,不能和 UI 共用,因为需要自己的 Update 调用。最好把上面这段放在 UI 的 Begin/End 之后。
好了,本节课学习到此结束,我是魔法阵维护师,关注我,下期更精彩!