上一章我们在我们的小游戏中加入了金币和背包,这一节课我们来往里面加入怪物,来丰富我们的小游戏

统一你的 Enemy 和 Slime 类

第一步:更新 Enemy.cs

把 Enemy.cs 完整替换为以下代码。注意看,它保留了事件系统,但数据结构改成了属性。

using System;

namespace MyGame
{
    class Enemy
    {
        public string Name { get; set; }
        public int Hp { get; set; }
        public int AttackPower { get; set; }
        public string DropItem { get; set; }

        // 地图坐标(新增)
        public int X { get; set; }
        public int Y { get; set; }
        public bool IsActive { get; set; }

        // 事件:死亡时触发
        public event Action<Enemy> OnEnemyKilled;

        public Enemy() { }  // 无参构造函数,备用

        public Enemy(string name, int hp, int attack, string drop)
        {
            Name = name;
            Hp = hp;
            AttackPower = attack;
            DropItem = drop;
            IsActive = true;
        }

        // 受伤逻辑
        public virtual void TakeDamage(int damage)
        {
            Hp = Hp - damage;
            Console.WriteLine(Name + " 受到了 " + damage + " 点伤害!剩余HP:" + Hp);

            if (Hp <= 0)
            {
                Die();
            }
        }

        // 死亡
        protected virtual void Die()
        {
            Console.WriteLine(Name + " 被消灭了!");
            IsActive = false;

            // 触发死亡事件
            OnEnemyKilled?.Invoke(this);
        }

        // 攻击(虚方法,子类可重写)
        public virtual void Attack()
        {
            Console.WriteLine(Name + " 发动攻击,造成 " + AttackPower + " 点伤害!");
        }
    }
}
第二步:更新 Slime.cs
using System;

namespace MyGame
{
    class Slime : Enemy
    {
        public Slime() { }  // 无参构造函数

        public Slime(string name, int hp, int attack, string drop)
            : base(name, hp, attack, drop)
        {
        }

        // 史莱姆特有的攻击方式
        public override void Attack()
        {
            Console.WriteLine(Name + " 跳起来砸向你!造成 " + AttackPower + " 点伤害。");
        }

        // 死亡时特定表现
        protected override void Die()
        {
            Console.WriteLine(Name + " 化为一滩液体...");
            IsActive = false;
            OnEnemyKilled?.Invoke(this);
        }
    }
}
第三步:创建战斗管理器 BattleManager.cs

战斗管理器负责回合制战斗流程。玩家碰到敌人后,战斗管理器接管控制,打完之后再回到地图模式。

using System;

namespace MyGame
{
    class BattleManager
    {
        // 执行一场战斗,返回 true 表示玩家胜利
        public bool StartBattle(Player player, Enemy enemy)
        {
            Console.Clear();
            Console.WriteLine("========================================");
            Console.WriteLine("   ⚔️ 遭遇了 " + enemy.Name + "! ⚔️");
            Console.WriteLine("========================================\n");

            int round = 1;

            // 回合制战斗循环
            while (player.Hp > 0 && enemy.Hp > 0)
            {
                Console.WriteLine("--- 第 " + round + " 回合 ---");

                // 玩家攻击
                Console.Write("你攻击! ");
                enemy.TakeDamage(player.AttackPower);

                if (enemy.Hp <= 0)
                {
                    Console.WriteLine("\n🏆 你击败了 " + enemy.Name + "!");
                    Console.WriteLine("敌人掉落了:" + enemy.DropItem);
                    Console.WriteLine("\n按任意键继续冒险...");
                    Console.ReadKey(true);
                    return true;  // 玩家赢
                }

                // 敌人反击
                Console.Write(enemy.Name + " 反击! ");
                enemy.Attack();
                player.TakeDamage(enemy.AttackPower);

                if (player.Hp <= 0)
                {
                    Console.WriteLine("\n💀 你被 " + enemy.Name + " 打败了...");
                    Console.WriteLine("\n按任意键结束游戏...");
                    Console.ReadKey(true);
                    return false;  // 玩家输
                }

                Console.WriteLine();  // 空行分隔回合
                round++;
            }

            return false;
        }
    }
}

第四步:改造 Player.cs,加入战斗属性

给你的 Player.cs 加上血量和攻击力。把原来的 Player.cs 完整替换为:

using System;

namespace MyGame
{
    class Player
    {
        public int X { get; set; }
        public int Y { get; set; }
        public char Symbol { get; private set; }
        public ConsoleColor Color { get; private set; }

        // 战斗属性(新增)
        public int Hp { get; set; }
        public int MaxHp { get; private set; }
        public int AttackPower { get; set; }

        private int previousX;
        private int previousY;

        public Player(int startX, int startY)
        {
            X = startX;
            Y = startY;
            previousX = X;
            previousY = Y;
            Symbol = '@';
            Color = ConsoleColor.Yellow;

            // 初始战斗属性
            MaxHp = 100;
            Hp = MaxHp;
            AttackPower = 15;
        }

        public void SavePosition()
        {
            previousX = X;
            previousY = Y;
        }

        public void EraseOld()
        {
            Console.SetCursorPosition(previousX, previousY);
            Console.Write(" ");
        }

        public void Draw()
        {
            Console.SetCursorPosition(X, Y);
            Console.ForegroundColor = Color;
            Console.Write(Symbol);
            Console.ResetColor();
        }

        public void Move(int dx, int dy, int minX, int minY, int maxX, int maxY)
        {
            SavePosition();
            int newX = X + dx;
            int newY = Y + dy;

            if (newX >= minX && newX <= maxX && newY >= minY && newY <= maxY)
            {
                X = newX;
                Y = newY;
            }
        }

        // 玩家受伤
        public void TakeDamage(int damage)
        {
            Hp = Hp - damage;
            Console.WriteLine("你受到了 " + damage + " 点伤害!剩余血量:" + Hp);
        }

        // 治疗
        public void Heal(int amount)
        {
            Hp = Hp + amount;
            if (Hp > MaxHp) Hp = MaxHp;
            Console.WriteLine("你恢复了 " + amount + " 点血量。当前血量:" + Hp);
        }
    }
}
第五步:把一切串起来 —— 改造 Game.cs

这是最核心的一步。把 Game.cs 完整替换为以下代码:

using System;
using System.Collections.Generic;
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!;

        public void Start()
        {
            Console.CursorVisible = false;
            Console.Title = "控制台RPG - 方向键移动,ESC退出";

            map = new GameMap(40, 20);
            player = new Player(map.Width / 2, map.Height / 2);
            input = new InputHandler();
            ui = new GameUI(map.Height);
            battleManager = new BattleManager();
            isRunning = true;

            score = 0;

            // 初始化背包和系统
            inventory = new Inventory();
            achievement = new AchievementSystem();
            sound = new SoundSystem();

            // 初始化金币
            coins = new List<Coin>();
            SpawnCoins(totalCoins);

            // 初始化敌人
            enemies = new List<Enemy>();
            SpawnEnemies(totalEnemies);

            // 首次绘制地图
            map.Draw();
            DrawAllStaticObjects();

            // 主循环
            while (isRunning && player.Hp > 0)
            {
                Update();
                Render();
                Thread.Sleep(30);
            }

            // 游戏结束
            Console.SetCursorPosition(0, map.Height + 4);
            if (player.Hp <= 0)
            {
                Console.WriteLine("你倒下了...游戏结束。");
            }
            else
            {
                Console.WriteLine("游戏结束!感谢游玩。");
            }
            Console.WriteLine("按任意键退出...");
            Console.ReadKey();
        }

        // ===== 生成 =====
        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.Quit:
                    isRunning = false;
                    break;
            }

            // 检测金币碰撞
            CheckCoinCollision();

            // 检测敌人碰撞
            CheckEnemyCollision();

            // 金币捡完了就刷新
            if (GetActiveCoinCount() == 0)
                SpawnCoins(totalCoins);

            // 敌人都打完了就刷新
            if (GetActiveEnemyCount() == 0)
                SpawnEnemies(totalEnemies);
        }

        // 金币碰撞
        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);
        }
    }
}

第六步:微调 UI

using System;

namespace MyGame
{
    class GameUI
    {
        private int uiLine;

        public GameUI(int mapHeight)
        {
            uiLine = mapHeight + 1;
        }

        public void Draw(int score, int coinCount, int bagCount, int hp, int maxHp)
        {
            // 清空 UI 行
            Console.SetCursorPosition(0, uiLine);
            Console.Write(new string(' ', Console.WindowWidth));
            Console.SetCursorPosition(0, uiLine + 1);
            Console.Write(new string(' ', Console.WindowWidth));

            // 分数
            Console.SetCursorPosition(2, uiLine);
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write($"mark 分数:{score}");

            // 金币
            Console.SetCursorPosition(18, uiLine);
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.Write($"coin 金币:{coinCount}");

            // 背包
            Console.SetCursorPosition(34, uiLine);
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.Write($"bag 背包:{bagCount}");

            // 血量
            Console.SetCursorPosition(50, uiLine);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write($"heart HP:{hp}/{maxHp}");

            Console.ResetColor();
        }
    }
}

同时确认 Inventory.cs 有一个公开属性 ItemCount:、

最后的运行结果,可以对照一下

  1. 地图上:黄色 @(玩家)、黄色 $(金币)、红色 S(史莱姆)

  2. 走到 $ 上:金币消失,分数+10,背包增加“金币”

  3. 走到 S 上:屏幕清空,进入回合制战斗

  4. 战斗中:你打一下,史莱姆打一下,直到一方倒下

  5. 战斗胜利:回到地图,史莱姆消失,背包获得掉落物,成就和音效系统触发

  6. 金币/敌人清空后:自动刷新,游戏无限进行

  7. 按 ESC:退出游戏

好了,今天的课到此结束,如果有报错或者什么问题,请在评论区留言,我每天都会看。关注我,下一期更精彩。

更多推荐