从零开发游戏需要学习的c#模块,第三十四章(设置界面)

本节课学习目标

  1. 标题画面增加"设置"选项

  2. 设置界面:音量滑块、全屏开关

  3. 设置保存到 JSON 文件

  4. 暂停菜单中也能打开设置

第一步:扩展存档数据

打开 SaveManager.cs,在 SaveData 类里加两个字段:

public class SaveData

{

public int HighScore { get; set; }

public int TotalCoinsCollected { get; set; }

public int TotalEnemiesDefeated { get; set; }

public float MusicVolume { get; set; } = 0.5f; // ★ 音量 0~1

public bool IsFullscreen { get; set; } = false; // ★ 是否全屏

}

第二步:创建设置界面类

右键项目 → 添加 ,文件名 SettingsMenu.cs

cs 复制代码
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using FontStashSharp;
using System.Collections.Generic;

namespace MY_FIRST_GAME
{
    public class SettingsMenu
    {
        private List<string> options;
        private int selectedIndex;
        private KeyboardState previousKeyboard;
        private KeyboardState currentKeyboard;
        private bool adjustingVolume;  // 正在调节音量

        public float Volume { get; set; }
        public bool IsFullscreen { get; set; }

        public SettingsMenu(float volume, bool isFullscreen)
        {
            Volume = volume;
            IsFullscreen = isFullscreen;

            options = new List<string>
            {
                "音量",
                "全屏",
                "返回"
            };
            selectedIndex = 0;
            adjustingVolume = false;
        }

        // 返回值:-1=还在设置, 0=返回, 1=音量变化, 2=全屏切换
        public int Update()
        {
            previousKeyboard = currentKeyboard;
            currentKeyboard = Keyboard.GetState();

            if (adjustingVolume)
            {
                if (IsKeyJustPressed(Keys.Left) || IsKeyJustPressed(Keys.A))
                {
                    Volume = MathHelper.Max(0, Volume - 0.1f);
                    return 1;
                }
                if (IsKeyJustPressed(Keys.Right) || IsKeyJustPressed(Keys.D))
                {
                    Volume = MathHelper.Min(1, Volume + 0.1f);
                    return 1;
                }
                if (IsKeyJustPressed(Keys.Enter) || IsKeyJustPressed(Keys.Escape))
                {
                    adjustingVolume = false;
                }
                return -1;
            }

            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))
            {
                switch (selectedIndex)
                {
                    case 0: // 音量
                        adjustingVolume = true;
                        break;
                    case 1: // 全屏
                        IsFullscreen = !IsFullscreen;
                        return 2;
                    case 2: // 返回
                        return 0;
                }
            }

            if (IsKeyJustPressed(Keys.Escape))
                return 0;

            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, 200) });
            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, 100), Color.White);

            // 音量选项
            Color volColor = (selectedIndex == 0) ? Color.Gold : Color.LightGray;
            string volText = (selectedIndex == 0) ? "> 音量 <" : "  音量  ";
            spriteBatch.DrawString(font, volText,
                new Vector2(400 - font.MeasureString(volText).X / 2, 200), volColor);

            // 音量条
            DrawSlider(spriteBatch, device, 250, 230, 300, 20, Volume);

            // 音量数值
            string volValue = $"{(int)(Volume * 100)}%";
            spriteBatch.DrawString(font, volValue,
                new Vector2(560, 230), Color.White);

            // 全屏选项
            Color fsColor = (selectedIndex == 1) ? Color.Gold : Color.LightGray;
            string fsText = (selectedIndex == 1) ? "> 全屏 <" : "  全屏  ";
            spriteBatch.DrawString(font, fsText,
                new Vector2(400 - font.MeasureString(fsText).X / 2, 300), fsColor);

            string fsValue = IsFullscreen ? "开" : "关";
            spriteBatch.DrawString(font, fsValue,
                new Vector2(560, 300), IsFullscreen ? Color.LimeGreen : Color.Gray);

            // 返回选项
            Color backColor = (selectedIndex == 2) ? Color.Gold : Color.LightGray;
            string backText = (selectedIndex == 2) ? "> 返回 <" : "  返回  ";
            spriteBatch.DrawString(font, backText,
                new Vector2(400 - font.MeasureString(backText).X / 2, 400), backColor);

            string hint = "↑↓ 选择 | 回车 确认 | ←→ 调节 | ESC 返回";
            spriteBatch.DrawString(font, hint,
                new Vector2(400 - font.MeasureString(hint).X / 2, 500), Color.Gray);
        }

        private void DrawSlider(SpriteBatch spriteBatch, GraphicsDevice device,
            int x, int y, int width, int height, float value)
        {
            Texture2D pixel = new Texture2D(device, 1, 1);
            pixel.SetData(new[] { Color.White });

            // 背景
            spriteBatch.Draw(pixel, new Rectangle(x, y, width, height), Color.DarkSlateGray);
            // 边框
            spriteBatch.Draw(pixel, new Rectangle(x, y, width, 2), Color.White);
            spriteBatch.Draw(pixel, new Rectangle(x, y + height - 2, width, 2), Color.White);
            spriteBatch.Draw(pixel, new Rectangle(x, y, 2, height), Color.White);
            spriteBatch.Draw(pixel, new Rectangle(x + width - 2, y, 2, height), Color.White);

            // 填充
            int fillWidth = (int)((width - 4) * value);
            spriteBatch.Draw(pixel, new Rectangle(x + 2, y + 2, fillWidth, height - 4), Color.Gold);

            // 滑块
            int sliderX = x + 2 + fillWidth - 5;
            spriteBatch.Draw(pixel, new Rectangle(sliderX, y - 4, 10, height + 8), Color.White);
        }

        public void Reset()
        {
            selectedIndex = 0;
            adjustingVolume = false;
        }
    }
}

第三步:改造 Game1.cs

1. 添加字段:

csharp

复制代码
private SettingsMenu settingsMenu = default!;
private bool inSettings = false;

2. 在 Initialize 里创建设置菜单:

csharp

复制代码
settingsMenu = new SettingsMenu(saveData.MusicVolume, saveData.IsFullscreen);
// 应用保存的设置
ApplySettings();

3. 添加应用设置的方法:

csharp

复制代码
private void ApplySettings()
{
    SoundEffect.MasterVolume = saveData.MusicVolume;
    _graphics.IsFullScreen = saveData.IsFullscreen;
    _graphics.ApplyChanges();
}

4. 在标题画面加"设置"选项:

把标题画面的空格触发改成菜单选择。简单做法:在 UpdateTitle 状态里加:

csharp

复制代码
case SceneType.Title:
    if (keyboard.IsKeyDown(Keys.Enter))
    {
        inSettings = true;
        settingsMenu = new SettingsMenu(saveData.MusicVolume, saveData.IsFullscreen);
        settingsMenu.Reset();
    }
    if (keyboard.IsKeyDown(Keys.Space) && !inSettings)
    {
        // 开始游戏...
    }
    break;

5. 处理设置更新:

Update 开头(暂停检查之后)加:

csharp

复制代码
if (inSettings)
{
    int result = settingsMenu.Update();
    if (result == 1) // 音量变化
    {
        saveData.MusicVolume = settingsMenu.Volume;
        SoundEffect.MasterVolume = settingsMenu.Volume;
    }
    else if (result == 2) // 全屏切换
    {
        saveData.IsFullscreen = settingsMenu.IsFullscreen;
        _graphics.IsFullScreen = settingsMenu.IsFullscreen;
        _graphics.ApplyChanges();
    }
    else if (result == 0) // 返回
    {
        SaveManager.Save(saveData);
        inSettings = false;
    }
}

6. 在 Draw 里画设置界面:

在所有绘制之后加:

csharp

复制代码
if (inSettings)
{
    _spriteBatch.Begin();
    settingsMenu.Draw(_spriteBatch, font, GraphicsDevice);
    _spriteBatch.End();
}

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

相关推荐
gc_22991 小时前
学习C#调用OpenXml操作word文档的基本用法(39:学习表格类-1)
c#·word·表格·table·openxml
FserSuN2 小时前
Machine Learning Specialization - Week 1, 9-20学习总结
人工智能·学习·机器学习
gc_22992 小时前
C#测试调用Net.Codecrete.QrCodeGenerator库生成二维码的基本用法
c#·二维码·qrcodegenerator
OBiO20132 小时前
肺部靶向 AAV 怎么选?如何解决靶向不精准、转导效率低的递送难题?
学习
我命由我123452 小时前
UGC、PGC、PUGC 极简理解
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
七老板的blog2 小时前
【Agent智能体】 任务规划工作流
python·学习·ai·开源
海兰2 小时前
【文字三国志:第五篇】天命重构,游戏前端UI设计
前端·人工智能·游戏·语言模型
海鸥-w2 小时前
前端学习python第二天手敲笔记整理
前端·python·学习
山楂树の3 小时前
Video核心术语
学习·音视频