从零开发游戏需要学习的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 里加入存档和读档逻辑

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 加两个新方法

cs 复制代码
// 静默添加(不打印"获得物品"),用于加载存档时批量恢复背包
public void AddItemSilent(string itemName)
{
    items.Add(itemName);
}

// 获取所有物品列表,用于存档
public List<string> GetAllItems()
{
    return new List<string>(items);  // 返回副本,保护原始数据
}

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

相关推荐
_李小白3 小时前
【android opencv学习笔记】Day 17: 目标追踪(MeanShift)
android·opencv·学习
一只机电自动化菜鸟3 小时前
一建机电备考笔记(40) 建筑机电施工—排水管道施工(含考频+题型)
经验分享·笔记·学习·职场和发展·课程设计
2301_818730563 小时前
numpy的学习(笔记)
学习·numpy
GHL2842710904 小时前
Logon failed, use ctrl+c to cancel basic credential prompt
学习·prompt
共享家95274 小时前
Langchain的学习(二)
学习·langchain
victory04314 小时前
agent 学习路径解析 学习资源分享
学习
2301_780789665 小时前
手游遇到攻击为什么要用SDK游戏盾手游遇到攻击为什么要用 SDK 游戏盾?
安全·web安全·游戏·架构·kubernetes·ddos
咸甜适中6 小时前
rust语言学习笔记Trait(八)Iterator(迭代器)
笔记·学习·rust
Xin_ye100866 小时前
C# 零基础到精通教程 - 第十一章:LINQ——语言集成查询
开发语言·c#