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

从零开发游戏需要学习的c#模块,第十四章(保存和加载)

这一章我们来讲一下如何在我们的小游戏中加入保存和加载的功能,让我们的游戏有记忆

第一步:完善GameSaveData.cs

using System.Collections.Generic;

namespace MyGame
{
class GameSaveData
{
public int PlayerX { get; set; }
public int PlayerY { get; set; }
public int PlayerHp { get; set; }
public int PlayerMaxHp { get; set; }
public int Score { get; set; }
public List<string> InventoryItems { get; set; } = new List<string>();
}
}

这里存了所有需要恢复的游戏状态:玩家位置、血量、分数、背包。

第二步:给InputHandler.cs加 S 和 L 键

打开InputHandler.cs,在Command枚举和GetCommand方法里加上存档和读档。

using System;

namespace MyGame
{
class InputHandler
{
public enum Command
{
None,
MoveUp,
MoveDown,
MoveLeft,
MoveRight,
Save, // ★ 新增
Load, // ★ 新增
Quit
}

public Command GetCommand()
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.UpArrow: return Command.MoveUp;
case ConsoleKey.DownArrow: return Command.MoveDown;
case ConsoleKey.LeftArrow: return Command.MoveLeft;
case ConsoleKey.RightArrow: return Command.MoveRight;
case ConsoleKey.S: return Command.Save; // ★ S 键存档
case ConsoleKey.L: return Command.Load; // ★ L 键读档
case ConsoleKey.Escape: return Command.Quit;
}
}
return Command.None;
}
}
}

第三步:在Game.cs里加入存档和读档逻辑

using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Threading; namespace MyGame { class Game { private static Game? instance; public static Game Instance { get { if (instance == null) instance = new Game(); return instance; } } private Game() { } private bool isRunning; private Player player = default!; private GameMap map = default!; private InputHandler input = default!; private GameUI ui = default!; private BattleManager battleManager = default!; private List<Coin> coins = default!; private int score; private int totalCoins = 5; private List<Enemy> enemies = default!; private int totalEnemies = 3; private Inventory inventory = default!; private AchievementSystem achievement = default!; private SoundSystem sound = default!; private string saveFilePath = "gamesave.json"; // ★ 存档文件路径 public void Start() { Console.CursorVisible = false; Console.Title = "控制台RPG - WASD移动,S存档 L读档,ESC退出"; map = new GameMap(40, 20); input = new InputHandler(); ui = new GameUI(map.Height); battleManager = new BattleManager(); achievement = new AchievementSystem(); sound = new SoundSystem(); // ★ 检查是否有存档 bool loaded = TryLoadGame(); if (!loaded) { // 新游戏:用默认值初始化 player = new Player(map.Width / 2, map.Height / 2); inventory = new Inventory(); score = 0; coins = new List<Coin>(); enemies = new List<Enemy>(); } isRunning = true; // 初始化地图内容(金币和敌人) if (coins.Count == 0) SpawnCoins(totalCoins); if (enemies.Count == 0) SpawnEnemies(totalEnemies); map.Draw(); DrawAllStaticObjects(); // 主循环 while (isRunning && player.Hp > 0) { Update(); Render(); Thread.Sleep(30); } // ★ 游戏结束自动存档 if (player.Hp > 0) { SaveGame(); } Console.SetCursorPosition(0, map.Height + 4); if (player.Hp <= 0) { Console.WriteLine("你倒下了...游戏结束。存档已保留,可加载重来。"); } else { Console.WriteLine("游戏结束!感谢游玩。"); } Console.WriteLine("按任意键退出..."); Console.ReadKey(); } // ===== ★ 保存游戏 ===== private void SaveGame() { GameSaveData data = new GameSaveData { PlayerX = player.X, PlayerY = player.Y, PlayerHp = player.Hp, PlayerMaxHp = player.MaxHp, Score = score, InventoryItems = inventory.GetAllItems() // 需要在 Inventory 里加这个方法 }; string json = JsonSerializer.Serialize(data); File.WriteAllText(saveFilePath, json); // 在 UI 上显示保存成功 Console.SetCursorPosition(0, map.Height + 3); Console.Write(new string(' ', Console.WindowWidth)); Console.SetCursorPosition(2, map.Height + 3); Console.ForegroundColor = ConsoleColor.Green; Console.Write("✅ 游戏已保存!"); Console.ResetColor(); } // ===== ★ 加载游戏 ===== private bool TryLoadGame() { if (!File.Exists(saveFilePath)) { Console.WriteLine("没有找到存档,开始新游戏。"); return false; } // 读取 JSON string json = File.ReadAllText(saveFilePath); GameSaveData? data = JsonSerializer.Deserialize<GameSaveData>(json); if (data == null) { Console.WriteLine("存档损坏,开始新游戏。"); return false; } // 用存档数据恢复游戏状态 player = new Player(data.PlayerX, data.PlayerY); player.Hp = data.PlayerHp; // MaxHp 通过构造函数设定,如需恢复请给 Player 加 SetMaxHp 方法 score = data.Score; inventory = new Inventory(); foreach (string item in data.InventoryItems) { inventory.AddItemSilent(item); // 需要在 Inventory 里加静默添加方法 } coins = new List<Coin>(); enemies = new List<Enemy>(); Console.WriteLine("存档已加载!欢迎回来!"); return true; } // ===== 生成 ===== private void SpawnCoins(int count) { Random rng = new Random(); for (int i = 0; i < count; i++) { int x = rng.Next(1, map.Width - 1); int y = rng.Next(1, map.Height - 1); coins.Add(new Coin(x, y)); } } private void SpawnEnemies(int count) { Random rng = new Random(); for (int i = 1; i <= count; i++) { int x = rng.Next(1, map.Width - 1); int y = rng.Next(1, map.Height - 1); Slime slime = new Slime("绿色史莱姆" + i, 25 + i * 5, 6 + i, "史莱姆粘液"); slime.X = x; slime.Y = y; slime.IsActive = true; slime.OnEnemyKilled += achievement.OnEnemyKilled; slime.OnEnemyKilled += sound.OnEnemyKilled; slime.OnEnemyKilled += (Enemy e) => { inventory.AddItem(e.DropItem); }; enemies.Add(slime); } } private void DrawAllStaticObjects() { foreach (Coin coin in coins) coin.Draw(); foreach (Enemy enemy in enemies) DrawEnemy(enemy); } private void DrawEnemy(Enemy enemy) { if (!enemy.IsActive) return; Console.SetCursorPosition(enemy.X, enemy.Y); Console.ForegroundColor = ConsoleColor.Red; Console.Write("S"); Console.ResetColor(); } private void EraseEnemy(Enemy enemy) { Console.SetCursorPosition(enemy.X, enemy.Y); Console.Write(" "); } // ===== 更新逻辑 ===== private void Update() { InputHandler.Command cmd = input.GetCommand(); switch (cmd) { case InputHandler.Command.MoveUp: player.Move(0, -1, 1, 1, map.Width - 2, map.Height - 2); break; case InputHandler.Command.MoveDown: player.Move(0, 1, 1, 1, map.Width - 2, map.Height - 2); break; case InputHandler.Command.MoveLeft: player.Move(-1, 0, 1, 1, map.Width - 2, map.Height - 2); break; case InputHandler.Command.MoveRight: player.Move(1, 0, 1, 1, map.Width - 2, map.Height - 2); break; case InputHandler.Command.Save: // ★ 存档 SaveGame(); break; case InputHandler.Command.Load: // ★ 读档 LoadGameFromKey(); break; case InputHandler.Command.Quit: isRunning = false; break; } CheckCoinCollision(); CheckEnemyCollision(); if (GetActiveCoinCount() == 0) SpawnCoins(totalCoins); if (GetActiveEnemyCount() == 0) SpawnEnemies(totalEnemies); } // ★ 按键读档 private void LoadGameFromKey() { Console.Clear(); bool loaded = TryLoadGame(); if (loaded) { map.Draw(); DrawAllStaticObjects(); } } private void CheckCoinCollision() { foreach (Coin coin in coins) { if (coin.IsActive && coin.X == player.X && coin.Y == player.Y) { coin.IsActive = false; coin.Erase(); score += coin.Value; inventory.AddItem("金币"); } } } private void CheckEnemyCollision() { foreach (Enemy enemy in enemies) { if (enemy.IsActive && enemy.X == player.X && enemy.Y == player.Y) { EraseEnemy(enemy); bool win = battleManager.StartBattle(player, enemy); if (!win) { isRunning = false; return; } Console.Clear(); map.Draw(); DrawAllStaticObjects(); } } } private int GetActiveCoinCount() { int count = 0; foreach (Coin coin in coins) if (coin.IsActive) count++; return count; } private int GetActiveEnemyCount() { int count = 0; foreach (Enemy enemy in enemies) if (enemy.IsActive) count++; return count; } private void Render() { player.EraseOld(); player.Draw(); ui.Draw(score, GetActiveCoinCount(), inventory.ItemCount, player.Hp, player.MaxHp); } } }

第四步:给Inventory.cs加两个新方法

// 静默添加(不打印"获得物品"),用于加载存档时批量恢复背包 public void AddItemSilent(string itemName) { items.Add(itemName); } // 获取所有物品列表,用于存档 public List<string> GetAllItems() { return new List<string>(items); // 返回副本,保护原始数据 }

好了,关于游戏的代码部分我们已经完成了很多了,下一章我们将会使用unity制作可以真正的2d游戏。关注我,下期更精彩!

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

相关文章:

  • 抖音视频怎么保存到相册去除抖音号?2026 实测去水印方法完整指南 - 科技热点发布
  • 对比按需计费与Token Plan套餐的成本控制感受
  • MATLAB与Simulink嵌入式视觉开发:从算法到硬件部署全流程解析
  • 2026年5月广安奢侈品回收商家推荐:避坑全攻略+2026最新回收行情 - 诚鑫名品
  • 不止于文本:用Gemini Pro Vision API玩转图片描述,附Python+Pillow完整代码
  • LinkBoy图形化编程环境向WCH微控制器的移植实践与优化
  • 魔兽争霸3现代系统适配终极方案:WarcraftHelper完全配置指南
  • 如何保存抖音图片并去水印?2026抖音图片去水印方法汇总与工具评测 - 科技热点发布
  • 对比直连与聚合路由在Taotoken平台上的稳定性体感差异
  • 用K210和MAX98357A做个会说话的小玩意儿:手把手教你播放自定义语音(附完整代码)
  • KLayout在macOS平台的深度技术解析:跨平台EDA工具的多环境部署策略
  • 【企业档案】深圳名探商务咨询有限公司基本工商信息与主营业务公示(2026版) - 我的节拍
  • 【语音检测】短时自相关的基音周期检测【含GUI Matlab源码 15451期】
  • 提示词优化与 Harness 性能的关系
  • 微信去水印小程序哪个最好用?2026年四款热门工具对比测评 - 科技热点发布
  • 如何快速掌握QuPath:数字病理图像分析的完整免费指南
  • 如何高效使用Translumo:专业用户的终极实时屏幕翻译配置指南
  • 【Perplexity摄影搜索效率提升300%】:基于NLP语义权重分析的6个专业级提示词模板
  • GDB 调试命令完整指南(ARM Cortex-M 嵌入式版)
  • 国产电脑与进口设备性能对比:15%差距背后的真实体验与部署实践
  • WSL2 + Ubuntu 22.04 环境下,保姆级配置Intel OneAPI 2024运行VASP测试包
  • 别只装Gurobi了!在MATLAB里用它和YALMIP跑通第一个优化模型的完整流程
  • 从Blend.exe到devenv.exe:一次搞懂VS2015里那两个让你困惑的启动项
  • 2026年家装艺术涂料代理商适配指南:品牌实力与区域服务能力全景分析 - 产业观察网
  • 海康明眸门禁SDK布防实战:Java回调函数里如何优雅处理人脸、考勤和测温数据?
  • Windows风扇控制终极方案:FanControl智能调速技术深度解析
  • 免费AI视频画质修复完整教程:Video2X让模糊视频重获新生
  • 告别虚拟机卡顿!用移动固态硬盘打造你的随身Ubuntu 22.04开发环境(保姆级分区教程)
  • 从O(n²)到O(n):阶乘求和算法的效率跃迁与竞赛实战解析
  • 告别命令行!用MobaXterm的X Server在Windows上流畅运行Linux的Firefox和Chrome