当前位置: 首页 > news >正文

从零开发游戏需要学习的c#模块,第二十三章(存档与高分系统)

本节课学习目标

  1. 用 JSON 保存最高分到文件

  2. 游戏结束时自动更新最高分

  3. 标题画面显示最高分

  4. 结束画面显示当前分数和最高分


第一步:创建存档管理类

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

using System.IO;
using System.Text.Json;

namespace MY_FIRST_GAME
{
public class SaveData
{
public int HighScore { get; set; }
public int TotalCoinsCollected { get; set; }
public int TotalEnemiesDefeated { get; set; }
}

public static class SaveManager
{
private static string filePath = "savedata.json";

public static SaveData Load()
{
if (File.Exists(filePath))
{
string json = File.ReadAllText(filePath);
SaveData? data = JsonSerializer.Deserialize<SaveData>(json);
return data ?? new SaveData();
}
return new SaveData();
}

public static void Save(SaveData data)
{
string json = JsonSerializer.Serialize(data);
File.WriteAllText(filePath, json);
}
}
}

第二步:完整替换Game1.cs

using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Audio; using System; using System.Collections.Generic; using System.IO; using FontStashSharp; namespace MY_FIRST_GAME { public class Game1 : Game { private GraphicsDeviceManager _graphics; private SpriteBatch _spriteBatch; private SceneManager sceneManager = default!; private Player player = default!; private Texture2D playerSpriteSheet; private Texture2D coinTexture; private List<Vector2> coins = default!; private Random rng = default!; private int score; private Texture2D enemyTexture; private List<Vector2> enemies = default!; private SpriteFontBase font = default!; private SpriteFontBase titleFont = default!; private SoundEffect coinSound = default!; private SoundEffect hitSound = default!; private ParticleSystem particleSystem = default!; private float titleTimer; // ★ 存档数据 private SaveData saveData = default!; private int coinsCollectedThisGame; private int enemiesDefeatedThisGame; private bool isNewHighScore; public Game1() { _graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; IsMouseVisible = true; } protected override void Initialize() { _graphics.PreferredBackBufferWidth = 800; _graphics.PreferredBackBufferHeight = 600; _graphics.ApplyChanges(); sceneManager = new SceneManager(); rng = new Random(); // ★ 加载存档 saveData = SaveManager.Load(); InitializeGame(); base.Initialize(); } private void InitializeGame() { coins = new List<Vector2>(); enemies = new List<Vector2>(); score = 0; coinsCollectedThisGame = 0; enemiesDefeatedThisGame = 0; isNewHighScore = false; titleTimer = 0f; SpawnCoins(5); SpawnEnemies(3); particleSystem = new ParticleSystem(GraphicsDevice); } protected override void LoadContent() { _spriteBatch = new SpriteBatch(GraphicsDevice); using var stream = File.OpenRead("Content/player_spritesheet.png"); playerSpriteSheet = Texture2D.FromStream(GraphicsDevice, stream); player = new Player(playerSpriteSheet, new Vector2(400, 300)); coinTexture = new Texture2D(GraphicsDevice, 24, 24); Color[] coinData = new Color[24 * 24]; for (int i = 0; i < coinData.Length; i++) coinData[i] = Color.Gold; coinTexture.SetData(coinData); enemyTexture = new Texture2D(GraphicsDevice, 40, 40); Color[] enemyData = new Color[40 * 40]; for (int i = 0; i < enemyData.Length; i++) enemyData[i] = Color.Red; enemyTexture.SetData(enemyData); var fontSystem = new FontSystem(); fontSystem.AddFont(File.ReadAllBytes("Content/consola.ttf")); font = fontSystem.GetFont(18); titleFont = fontSystem.GetFont(48); try { using var coinStream = File.OpenRead("Content/coin.wav"); coinSound = SoundEffect.FromStream(coinStream); using var hitStream = File.OpenRead("Content/hit.wav"); hitSound = SoundEffect.FromStream(hitStream); } catch { } } private void SpawnCoins(int count) { for (int i = 0; i < count; i++) coins.Add(new Vector2(rng.Next(50, 750), rng.Next(50, 550))); } private void SpawnEnemies(int count) { for (int i = 0; i < count; i++) enemies.Add(new Vector2(rng.Next(80, 720), rng.Next(80, 520))); } protected override void Update(GameTime gameTime) { float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; KeyboardState keyboard = Keyboard.GetState(); titleTimer += deltaTime; sceneManager.Update(); switch (sceneManager.CurrentScene) { case SceneType.Title: if (Keyboard.GetState().IsKeyDown(Keys.Space) && sceneManager.CurrentScene == SceneType.Title) { player = new Player(playerSpriteSheet, new Vector2(400, 300)); InitializeGame(); sceneManager.GoToGame(); } break; case SceneType.Game: player.Update(deltaTime); CheckCoinCollision(); CheckEnemyCollision(); if (coins.Count == 0) SpawnCoins(5); if (enemies.Count == 0) SpawnEnemies(3); particleSystem.Update(deltaTime); if (player.Hp <= 0) { // ★ 检测并保存最高分 if (score > saveData.HighScore) { saveData.HighScore = score; isNewHighScore = true; } saveData.TotalCoinsCollected += coinsCollectedThisGame; saveData.TotalEnemiesDefeated += enemiesDefeatedThisGame; SaveManager.Save(saveData); sceneManager.GoToGameOver(); } break; case SceneType.GameOver: break; } if (keyboard.IsKeyDown(Keys.M)) SoundEffect.MasterVolume = SoundEffect.MasterVolume > 0 ? 0f : 1f; if (keyboard.IsKeyDown(Keys.Escape)) Exit(); base.Update(gameTime); } private void CheckCoinCollision() { Rectangle playerRect = player.GetBounds(); for (int i = coins.Count - 1; i >= 0; i--) { Rectangle coinRect = new Rectangle((int)coins[i].X, (int)coins[i].Y, 24, 24); if (playerRect.Intersects(coinRect)) { Vector2 coinPos = coins[i]; coins.RemoveAt(i); score += 10; coinsCollectedThisGame++; particleSystem.EmitCoinParticles(coinPos); try { coinSound?.Play(); } catch { } } } } private void CheckEnemyCollision() { Rectangle playerRect = player.GetBounds(); for (int i = enemies.Count - 1; i >= 0; i--) { Rectangle enemyRect = new Rectangle( (int)enemies[i].X - 20, (int)enemies[i].Y - 20, 40, 40); if (playerRect.Intersects(enemyRect)) { Vector2 enemyPos = enemies[i]; enemies.RemoveAt(i); score += 50; enemiesDefeatedThisGame++; player.Hp -= 15; particleSystem.EmitHitParticles(enemyPos); try { hitSound?.Play(); } catch { } } } } protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.CornflowerBlue); _spriteBatch.Begin(); switch (sceneManager.CurrentScene) { case SceneType.Title: DrawTitle(); break; case SceneType.Game: DrawGame(); break; case SceneType.GameOver: DrawGameOver(); break; } _spriteBatch.End(); base.Draw(gameTime); } private void DrawTitle() { string title = "史莱姆猎人"; Vector2 titleSize = titleFont.MeasureString(title); Vector2 titlePos = new Vector2(400 - titleSize.X / 2, 120); float alpha = (float)(Math.Sin(titleTimer * 3) + 1) / 2 * 0.5f + 0.5f; _spriteBatch.DrawString(titleFont, title, titlePos, Color.Gold * alpha); string subtitle = "收集金币,消灭史莱姆!"; Vector2 subSize = font.MeasureString(subtitle); _spriteBatch.DrawString(font, subtitle, new Vector2(400 - subSize.X / 2, 200), Color.LightGray); // ★ 显示最高分 _spriteBatch.DrawString(font, $"🏆 最高分:{saveData.HighScore}", new Vector2(300, 260), Color.Gold); _spriteBatch.DrawString(font, $"💰 累计金币:{saveData.TotalCoinsCollected}", new Vector2(300, 285), Color.Yellow); _spriteBatch.DrawString(font, $"💀 累计击杀:{saveData.TotalEnemiesDefeated}", new Vector2(300, 310), Color.Red); string prompt = "按 空格键 开始游戏"; Vector2 promptSize = font.MeasureString(prompt); _spriteBatch.DrawString(font, prompt, new Vector2(400 - promptSize.X / 2, 400), Color.White); } private void DrawGame() { foreach (Vector2 coinPos in coins) _spriteBatch.Draw(coinTexture, coinPos, Color.White); foreach (Vector2 enemyPos in enemies) _spriteBatch.Draw(enemyTexture, enemyPos, null, Color.White, 0f, new Vector2(20, 20), 1f, SpriteEffects.None, 0f); particleSystem.Draw(_spriteBatch); player.Draw(_spriteBatch); _spriteBatch.DrawString(font, $"分数:{score}", new Vector2(10, 10), Color.White); _spriteBatch.DrawString(font, $"最高分:{saveData.HighScore}", new Vector2(10, 35), Color.Gold); _spriteBatch.DrawString(font, $"HP:{player.Hp}/{player.MaxHp}", new Vector2(10, 60), Color.LimeGreen); _spriteBatch.DrawString(font, $"本局金币:{coinsCollectedThisGame}", new Vector2(10, 85), Color.Yellow); _spriteBatch.DrawString(font, "WASD移动 | M静音 | ESC退出", new Vector2(10, 570), Color.LightGray); } private void DrawGameOver() { string title = "游戏结束"; Vector2 titleSize = titleFont.MeasureString(title); _spriteBatch.DrawString(titleFont, title, new Vector2(400 - titleSize.X / 2, 100), Color.Red); string scoreText = $"本局分数:{score}"; Vector2 scoreSize = font.MeasureString(scoreText); _spriteBatch.DrawString(font, scoreText, new Vector2(400 - scoreSize.X / 2, 190), Color.White); string highScoreText = $"🏆 最高分:{saveData.HighScore}"; Vector2 hsSize = font.MeasureString(highScoreText); _spriteBatch.DrawString(font, highScoreText, new Vector2(400 - hsSize.X / 2, 220), Color.Gold); // ★ 新纪录提示 if (isNewHighScore) { string newRecord = "🎉 新纪录!"; Vector2 nrSize = font.MeasureString(newRecord); _spriteBatch.DrawString(font, newRecord, new Vector2(400 - nrSize.X / 2, 255), Color.Orange); } string coinsText = $"本局收集金币:{coinsCollectedThisGame}"; Vector2 cSize = font.MeasureString(coinsText); _spriteBatch.DrawString(font, coinsText, new Vector2(400 - cSize.X / 2, 290), Color.Yellow); string enemiesText = $"本局消灭敌人:{enemiesDefeatedThisGame}"; Vector2 eSize = font.MeasureString(enemiesText); _spriteBatch.DrawString(font, enemiesText, new Vector2(400 - eSize.X / 2, 315), Color.Red); string prompt = "按 空格键 返回标题画面"; Vector2 promptSize = font.MeasureString(prompt); _spriteBatch.DrawString(font, prompt, new Vector2(400 - promptSize.X / 2, 400), Color.White); } } }

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

http://www.jsqmd.com/news/880000/

相关文章:

  • C#学习(26_05_24)
  • 环境变量助手
  • 【切负荷】计及切负荷和直流潮流(DC-OPF)风-火-储经济调度模型研究【IEEE24节点】附Python代码
  • 卖工业铝型材怎么找客户?下游工厂在哪里
  • 魔兽争霸3终极兼容解决方案:5分钟让经典游戏重获新生!
  • PHP文件包含漏洞利用实战:从LFI/RFI到图片马与Webshell载荷选型
  • NISQ时代量子机器学习实战:从变分量子电路到混合架构落地
  • 机器学习稳定性:从拓扑与度量空间视角看模型鲁棒性
  • Taotoken的API Key管理与审计日志功能实践体验
  • 如何快速实现网盘下载加速:终极网盘直链下载助手指南
  • 日志爆炸时代如何不被淹没?DeepSeek智能分析方案全链路实操,含Prometheus+Loki+DeepSeek三端联调手册
  • 上海篇:2026上海企业GEO优化实力榜单与全意图方法论解码 - GEO优化
  • 【图像去噪】基于交替方向乘子法(ADMM)、增广拉格朗日乘子法和软阈值算子和广义最小最大凹函数(GMC)惩罚实现图像去噪附matlab代码
  • Chrome抓包失败原因与Burp代理设置全解析
  • 【无人机避障】基于控制障碍函数CBF和卡尔曼滤波实现无人机精准轨迹跟踪 + 静态 动态障碍物实时避障附Matlab代码和Simulink
  • 【车辆路径规划】基于RRT算法的车辆导航工具箱实现附matlab代码
  • CVE漏洞编号规范与FortiSandbox安全机制解析
  • 【权威认证架构白皮书】:DeepSeek IDaaS集成标准v2.3发布,仅限首批200家ISV获取
  • 别错过机会!2026亲测靠谱的AI论文写作工具|避坑版
  • 每日热门skill:你的AI终于有“脑子“了!Memory MCP Server让Claude记住你的一切
  • 基于减法优化算法(SABO)优化CNN-BiGUR-Attention风电功率预测研究附Matlab代码
  • 后端架构技术01-「10万并发压垮线程池?Project Loom虚拟线程:一个线程几KB,轻松扛住流量洪峰」
  • math 7 [review] 2026.05.24
  • 如何用GHelper实现华硕笔记本性能与静音的完美平衡
  • 重构企业增长坐标:2026年全国GEO服务商实力图谱与选型深度洞察 - GEO优化
  • 【无人机三维路径规划】基于circle序列和正余弦策略的APO和CO算法无人机集群路径规划附Matlab代码
  • TVA视觉智能体专栏(二):为什么你的YOLO项目越用越废?对比TVA智能体四大核心差距
  • Solid.js信号驱动架构深度解析:告别虚拟DOM的真正实践
  • 开源AI工具选型血泪史:从LLM微调到RAG部署,我踩过的7个合规性、可审计性与SLA陷阱
  • 2026杭州GEO优化公司深度评测:从“流量收割”到“全意图增长”的战略选型指南 - GEO优化