上一章我们在我们的小游戏中加入了金币和背包,这一节课我们来往里面加入怪物,来丰富我们的小游戏
统一你的 Enemy 和 Slime 类
第一步:更新 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:、
最后的运行结果,可以对照一下
-
地图上 :黄色
@(玩家)、黄色$(金币)、红色S(史莱姆) -
走到
$上:金币消失,分数+10,背包增加"金币" -
走到
S上:屏幕清空,进入回合制战斗 -
战斗中:你打一下,史莱姆打一下,直到一方倒下
-
战斗胜利:回到地图,史莱姆消失,背包获得掉落物,成就和音效系统触发
-
金币/敌人清空后:自动刷新,游戏无限进行
-
按 ESC:退出游戏
好了,今天的课到此结束,如果有报错或者什么问题,请在评论区留言,我每天都会看。关注我,下一期更精彩。