从零开发游戏需要学习的c#模块,第三十三章(暂停菜单)

本节课目标

  1. ESCP 键暂停游戏

  2. 暂停时画面变暗,显示菜单选项

  3. 选项:继续游戏、返回标题、退出

  4. 暂停时所有敌人和子弹停止移动


第一步:创建暂停菜单类

右键项目 → 添加 ,文件名 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

  1. 添加字段:

csharp

private PauseMenu pauseMenu = default!;

private bool isPaused = false;

  1. 在 Initialize 里创建暂停菜单:

csharp

pauseMenu = new PauseMenu();

  1. 在 Update 开头处理暂停键:

在所有 switch 之前加:

csharp

// ★ 暂停切换

if (keyboard.IsKeyDown(Keys.P) && !isPaused ||

keyboard.IsKeyDown(Keys.Escape) && sceneManager.CurrentScene == SceneType.Game)

{

isPaused = !isPaused;

pauseMenu.Reset();

}

  1. 把 Game 状态的更新包裹在暂停检查里:

csharp

case SceneType.Game:

if (!isPaused)

{

// 原有的所有 Game 更新逻辑

}

break;

也就是把 case SceneType.Game: 里面那堆代码外面包一层 if (!isPaused)。

  1. 在 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 之后。

好了,本节课学习到此结束,我是魔法阵维护师,关注我,下期更精彩!

相关推荐
彧azz5 小时前
图的存储结构详解:邻接矩阵的原理、实现与应用
开发语言·数据结构·学习·php
平头哥AI5 小时前
Day 22 _ 包装错误别丢链_%w、errors.Is 与 errors.As
android·服务器·学习·golang·go
一阵寒风5 小时前
CAM智能化助力PCB 智能制造-培训体系第六章.项目开发学习
学习·制造
传奇开心果编程6 小时前
【Xilem 0.4 基础语法学与练】第15课:状态管理与 memoize 性能优化
学习·rust·前端框架
具身AGI6 小时前
视频即仿真,物理AI 人类学习路线 的下一步
人工智能·学习
wuyk5557 小时前
从零吃透 MQTT 通信|第 8 章 FreeRTOS 多任务架构下 MQTT 工程架构,任务拆分、队列解耦、临界区保护
c语言·开发语言·stm32·学习·架构
陈年老古董7 小时前
PyTorch食物图像分类实战:从数据集制作到CNN模型训练全流程详解
pytorch·深度学习·学习·机器学习·分类·cnn
明志数科7 小时前
从300克Ego头环看第一人称数据采集趋势:设备轻量化之后,场景端壁垒在哪
数码相机·学习
笨鸟先飞的橘猫8 小时前
系统设计第十七天决策卡
学习·游戏
Sunshing159 小时前
Lyapunov方程系统本身稳定性判定与镇定性判定
笔记·学习