本节课学习目标
-
标题画面增加"设置"选项
-
设置界面:音量滑块、全屏开关
-
设置保存到 JSON 文件
-
暂停菜单中也能打开设置
第一步:扩展存档数据
打开 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. 在标题画面加"设置"选项:
把标题画面的空格触发改成菜单选择。简单做法:在 Update 的 Title 状态里加:
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();
}
本节课学习到此结束,我叫魔法阵维护师,关注我,下期更精彩!