从零开发游戏需要学习的c#模块,第十三章(rpg小游戏入门,下篇,地图敌人与战斗触发)

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

统一你的 EnemySlime

第一步:更新 Enemy.cs

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

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
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 完整替换为:

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:退出游戏

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

相关推荐
. . . . .7 小时前
业务知识学习
学习
_Evan_Yao7 小时前
如何搭建属于自己的技术博客(CSDN / GitHub Pages)
后端·学习·github
炽烈小老头7 小时前
【每天学习一点算法 2026/05/21】课程表
学习·算法
月巴月巴白勺合鸟月半7 小时前
使用RAG完成一个基于本地的知识库的问答
c#
Harm灬小海7 小时前
【云计算学习之路】学习Centos7系统-Linux软件包管理
linux·运维·服务器·学习·云计算·yum·rpm
魔法阵维护师8 小时前
从零开发游戏需要学习的c#模块,第十七章(显示真正的图片——精灵绘制)
学习·游戏
婷婷_1728 小时前
JTAG (IEEE 1149.1)学习记录
学习·程序人生·debug·芯片·jtag·phy·eth/pcie
ygkl96988 小时前
未完待续 模拟题
学习
HwJack208 小时前
HarmonyOS NEXT 游戏APP开发中如何正确拦截退出手势
游戏·华为·harmonyos