如何在C#控制台游戏中实现类似Final Fantasy的多敌人战斗?
嘿,哥们!看你在把单敌人战斗改成FF那种多敌人系统的时候,碰到单敌人场景就出问题——菜单不显示、没法攻击,这事儿我以前改类似系统的时候也遇到过,咱们来一步步捋清楚问题在哪,然后搞定它!
首先,你原来的代码用的是单个Enemy enemy变量,改成多敌人的时候肯定得换成集合(比如List<Enemy>),但估计你改的时候没彻底统一逻辑,导致单敌人场景下出现空引用或者判断错误。咱们从核心逻辑开始重构:
第一步:把单个敌人变量换成敌人列表
把原来的单个敌人变量替换成全局的敌人列表,确保所有战斗逻辑都基于这个列表处理:
private List<Enemy> enemies = new List<Enemy>(); private Random rand = new Random(); // 把Random实例设为全局,避免重复初始化导致随机数重复
第二步:重构随机遇敌方法,支持1到多个敌人
原来的randomEncounter只生成单个敌人,现在改成随机生成1-3个敌人(数量范围你可以自己调),并统一加入列表:
public void randomEncounter() { Console.Clear(); enemies.Clear(); // 清空上一场战斗的残留敌人 int enemyCount = rand.Next(1, 4); // 随机生成1-3个敌人 Console.WriteLine($"你遭遇了 {enemyCount} 个敌人!\n"); for (int i = 0; i < enemyCount; i++) { int enemyType = rand.Next(1, 4); Enemy newEnemy = null; switch (enemyType) { case 1: newEnemy = new Enemy("巨魔", 1, this); newEnemy.setExpGiven(rand.Next(20, 30)); Console.WriteLine("一只巨魔出现了。"); break; case 2: newEnemy = new Enemy("兽人", 1, this); newEnemy.setExpGiven(rand.Next(30, 50)); Console.WriteLine("一只兽人出现了。"); break; case 3: newEnemy = new Enemy("食人魔", 1, this); newEnemy.setExpGiven(rand.Next(50, 70)); Console.WriteLine("一只食人魔出现了。"); break; } if (newEnemy != null) { enemies.Add(newEnemy); } } Console.WriteLine(); }
第三步:添加辅助方法判断所有敌人是否死亡
原来的enemy.isDead()只能判断单个敌人,现在需要一个方法检查列表里所有敌人是否都被击败:
private bool AllEnemiesDead() { // 只要有一个敌人存活,就返回false;全死了才返回true return enemies.All(enemy => enemy.isDead()); }
第四步:重构战斗逻辑,支持选择攻击目标
原来的fight()方法直接打单个敌人,现在得让玩家选择要攻击的存活敌人,统一单/多敌人的战斗流程:
public void fight() { // 先过滤出活着的敌人 var aliveEnemies = enemies.Where(e => !e.isDead()).ToList(); if (aliveEnemies.Count == 0) { Console.WriteLine("所有敌人都已经死透了!"); return; } // 显示可选攻击目标 Console.WriteLine("选择要攻击的敌人:"); for (int i = 0; i < aliveEnemies.Count; i++) { Console.WriteLine($"{i+1}. {aliveEnemies[i].Name} - 生命值:{aliveEnemies[i].Health}"); } try { Console.Write("输入你的选择:"); int targetIndex = Convert.ToInt32(Console.ReadLine()) - 1; // 转成列表索引(从0开始) if (targetIndex >= 0 && targetIndex < aliveEnemies.Count) { Enemy target = aliveEnemies[targetIndex]; // 这里插入你的玩家攻击逻辑,比如 player.Attack(target); Console.WriteLine($"你攻击了 {target.Name}!"); // 敌人反击(如果还活着) if (!target.isDead()) { Console.WriteLine($"{target.Name} 对你发起了反击!"); // target.Attack(player); } else { Console.WriteLine($"{target.Name} 被你打倒了!"); } } else { Console.WriteLine("无效的目标选择!"); } } catch { Console.WriteLine("输入无效!请输入数字。"); } }
第五步:修复战斗菜单的逻辑问题
原来的encounterMenu有几个坑:比如逃离后没直接终止循环、敌人死亡判断还是用单个变量、菜单没显示敌人状态。现在修改成统一的逻辑:
public void encounterMenu() { randomEncounter(); do { // 绘制玩家和所有敌人的血条 drawHealth(); Console.WriteLine("敌人状态:"); foreach (var enemy in enemies) { Console.WriteLine($"- {enemy.Name}: {(enemy.isDead() ? "已死亡" : $"{enemy.Health} HP")}"); } Console.WriteLine("\n1. 战斗"); Console.WriteLine("2. 治疗"); Console.WriteLine("3. 升级"); Console.WriteLine("4. 逃跑\n"); try { Console.Write("你想做什么?:"); choice = Convert.ToInt32(Console.ReadLine()); Console.WriteLine(); switch (choice) { case 1: fight(); break; case 2: heal(); break; case 3: player.levelUp(); break; case 4: Console.Clear(); mainMenu(); return; // 逃跑后直接返回主菜单,终止战斗循环 default: Console.WriteLine("无效的选择!\n"); break; } } catch { Console.WriteLine("\n你什么都没输入!\n"); } pause(); Console.Clear(); // 检查所有敌人是否死亡,是的话触发新遇敌 if (AllEnemiesDead()) { Console.WriteLine("所有敌人都被你击败了!获得经验值!"); // 给玩家累加所有敌人的经验 foreach (var enemy in enemies) { player.AddExp(enemy.getExpGiven()); } pause(); randomEncounter(); } // 检查玩家是否死亡,死了就结束战斗 if (player.isDead()) { Console.WriteLine("你死了!游戏结束。"); pause(); mainMenu(); return; } } while (true); // 无限循环直到逃跑或玩家死亡 }
为什么你原来的单敌人场景会出问题?
大概率是你没彻底抛弃单个enemy变量,比如在判断死亡、绘制菜单的时候,还是混用了原来的单个变量和新的敌人列表,导致单敌人场景下出现空引用(比如列表里有一个敌人,但你还是去访问enemy变量,而它是null)。现在统一用列表处理,不管是1个还是多个敌人,逻辑都是完全一致的,就不会出现场景差异的问题了。
要是drawHealth()需要显示敌人的血条,你也可以在这个方法里遍历enemies列表来绘制就行。
内容的提问来源于stack exchange,提问作者Rocco

