news 2026/5/18 22:00:16

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

作者头像

张小明

前端开发工程师

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

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

统一你的EnemySlime

第一步:更新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:退出游戏

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/18 21:59:34

【收藏备用|2026年版】35岁学AI太晚?看完这篇,小白也能逆袭算法岗

“我32岁学AI是不是太老了&#xff1f;”上周刚拿到某头部互联网公司算法岗offer的李哥&#xff0c;入职当天才发现自己是组里最“年轻”的那个——带他的导师37岁&#xff0c;团队里还有两位35的前辈&#xff0c;甚至有一位42岁的资深工程师&#xff0c;专门负责大模型落地对接…

作者头像 李华
网站建设 2026/5/18 21:52:53

SECS4Net完整指南:在.NET中快速实现半导体设备通信系统

SECS4Net完整指南&#xff1a;在.NET中快速实现半导体设备通信系统 【免费下载链接】secs4net SECS-II/HSMS-SS/GEM implementation on .NET 项目地址: https://gitcode.com/gh_mirrors/se/secs4net SECS4Net是一个基于.NET平台的开源库&#xff0c;完整实现了SEMI标准的…

作者头像 李华
网站建设 2026/5/18 21:46:36

Taotoken控制台用量看板带来的成本洞察与优化启示

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Taotoken控制台用量看板带来的成本洞察与优化启示 1. 从接入到观测&#xff1a;成本透明化的起点 在技术项目中引入大模型能力&am…

作者头像 李华
网站建设 2026/5/18 21:43:38

LabVIEW集成Python虚拟环境:基于Conda的隔离部署与工程实践

1. 项目概述&#xff1a;当LabVIEW遇上Python虚拟环境如果你是一名LabVIEW开发者&#xff0c;最近是不是经常听到团队里讨论Python&#xff1f;或者你自己也遇到了这样的场景&#xff1a;一个复杂的算法&#xff0c;用G语言实现起来异常繁琐&#xff0c;但Python社区里却有现成…

作者头像 李华
网站建设 2026/5/18 21:42:08

为团队统一配置 Taotoken CLI 以提升开发协作效率

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 为团队统一配置 Taotoken CLI 以提升开发协作效率 当一个技术团队开始在日常开发中集成大模型能力时&#xff0c;往往会面临一些共…

作者头像 李华