前言前面第四篇已经详细介绍了Ai逻辑引擎的方法下面介绍下Ai引擎怎样设计的方便后续做逻辑时可以更好理解游戏自动走.代码如下import { Unit, Faction, Position } from ../common/GameModels; import { GameEngine } from ../engine/GameEngine; import { GamePhase } from ../common/GameConstants; // AI引擎 - 控制敌方单位行动 export class AIEngine { private gameEngine: GameEngine; constructor(gameEngine: GameEngine) { this.gameEngine gameEngine; } // 执行AI回合 executeTurn(): Promisevoid { return new Promise((resolve: () void): void { const enemyUnits: Unit[] this.gameEngine.getGameState().units .filter((u: Unit): boolean u.faction Faction.ENEMY u.isAlive !u.hasActed); if (enemyUnits.length 0) { resolve(); return; } // 逐个执行敌方单位行动 this.executeUnitActions(enemyUnits, 0, resolve); }); } // 逐个执行单位行动带延迟以便观察 private executeUnitActions(units: Unit[], index: number, resolve: () void): void { if (index units.length) { resolve(); return; } const unit units[index]; if (!unit.isAlive || unit.hasActed) { this.executeUnitActions(units, index 1, resolve); return; } // 执行当前单位的行动 this.executeUnitAction(unit); // 延迟后执行下一个单位 setTimeout((): void { this.executeUnitActions(units, index 1, resolve); }, 800); } // 执行单个单位的行动 private executeUnitAction(unit: Unit): void { // 1. 尝试攻击优先攻击 const attackTarget this.findBestAttackTarget(unit); if (attackTarget) { this.gameEngine.attackUnit(unit, attackTarget); return; } // 2. 如果没有攻击目标则移动 const moveTarget this.findBestMoveTarget(unit); if (moveTarget) { this.gameEngine.moveUnit(unit, moveTarget); // 移动后尝试攻击 const newAttackTarget this.findBestAttackTarget(unit); if (newAttackTarget) { this.gameEngine.attackUnit(unit, newAttackTarget); } else { unit.hasActed true; } } else { // 无法移动标记为已行动 unit.hasActed true; } } // 寻找最佳攻击目标 private findBestAttackTarget(unit: Unit): Position | null { const attackablePositions: Position[] this.gameEngine.getAttackablePositions(unit); if (attackablePositions.length 0) return null; // 评估每个攻击目标的价值 let bestTarget: Position | null null; let bestValue -1; for (let i 0; i attackablePositions.length; i) { const pos attackablePositions[i]; const targetUnit this.gameEngine.getUnitAt(pos); if (!targetUnit) continue; // 计算目标价值优先攻击血量低、攻击力高的单位 const value this.evaluateTargetValue(unit, targetUnit); if (value bestValue) { bestValue value; bestTarget pos; } } return bestTarget; } // 评估攻击目标的价值 private evaluateTargetValue(attacker: Unit, target: Unit): number { let value 0; // 1. 优先攻击将军高价值目标 if (target.type 4) { // GENERAL value 100; } // 2. 优先攻击血量低的单位可以击杀 const hpPercent target.hp / target.maxHp; if (hpPercent 0.3) { value 50; // 可能击杀 } // 3. 考虑攻击力威胁 value target.attack * 0.5; // 4. 远程单位优先攻击近战单位 if (attacker.type 1 || attacker.type 3) { // ARCHER or MAGE if (target.type 0 || target.type 2) { // WARRIOR or CAVALRY value 20; } } // 5. 扣除距离惩罚越近越好 const distance Math.abs(attacker.position.x - target.position.x) Math.abs(attacker.position.y - target.position.y); value - distance * 2; return value; } // 寻找最佳移动目标 private findBestMoveTarget(unit: Unit): Position | null { const movablePositions: Position[] this.gameEngine.getMovablePositions(unit); if (movablePositions.length 0) return null; // 找到最近的敌方单位 const enemyUnits: Unit[] this.gameEngine.getGameState().units .filter((u: Unit): boolean u.faction Faction.PLAYER u.isAlive); if (enemyUnits.length 0) return null; // 评估每个可移动位置的价值 let bestPosition: Position | null null; let bestValue -1000; for (let i 0; i movablePositions.length; i) { const pos movablePositions[i]; const value this.evaluateMovePosition(unit, pos, enemyUnits); if (value bestValue) { bestValue value; bestPosition pos; } } return bestPosition; } // 评估移动位置的价值 private evaluateMovePosition(unit: Unit, pos: Position, enemyUnits: Unit[]): number { let value 0; // 1. 接近敌方单位移动范围允许的情况下 let minDistanceToEnemy 100; for (let i 0; i enemyUnits.length; i) { const enemy enemyUnits[i]; const distance Math.abs(pos.x - enemy.position.x) Math.abs(pos.y - enemy.position.y); minDistanceToEnemy Math.min(minDistanceToEnemy, distance); } // 对于近战单位想要接近敌人 if (unit.attackRange 1) { value (10 - minDistanceToEnemy) * 10; // 越近越好 } // 对于远程单位保持距离但进入攻击范围 if (unit.attackRange 1) { if (minDistanceToEnemy unit.attackRange minDistanceToEnemy 1) { value 50; // 理想攻击位置 } else if (minDistanceToEnemy unit.attackRange) { value (unit.attackRange - minDistanceToEnemy 10) * 5; // 需要接近 } } // 2. 优先选择有利地形 const terrain this.gameEngine.getTerrainAt(pos); if (terrain 1) { // FOREST value 10; // 防御加成 } else if (terrain 2) { // MOUNTAIN value 15; // 更多防御加成 } else if (terrain 3) { // WATER value - 100; // 避免水域 } // 3. 避免过于冒险低血量单位应该更保守 const hpPercent unit.hp / unit.maxHp; if (hpPercent 0.3) { // 低血量寻找防御位置 if (terrain 1 || terrain 2) { value 30; } // 远离敌方 value minDistanceToEnemy * 3; } return value; } }一、游戏 AI 的核心问题游戏 AI 需要回答三个问题1. **做什么**攻击移动等待2. **攻击谁**选哪个目标最有价值3. **去哪里**移动到哪个格子最有利下面AIEngine.ets 用 **贪心策略Greedy Algorithm** 解答了这三个问题。二、AI 决策流程全景AI 回合开始 ↓ 获取所有存活且未行动的敌方单位 ↓ 逐个单位决策间隔800ms ↓ ┌─ 当前单位 ├─ 1. 能直接攻击 │ 是 → 选最高价值目标 → 攻击 → 结束行动 │ 否 ↓ ├─ 2. 能移动 │ 是 → 选最佳移动位置 → 移动 │ ↓ │ 移动后能攻击 │ 是 → 攻击 → 结束行动 │ 否 → 标记已行动 │ 否 → 标记已行动 AI 回合结束三、异步回合执行Promise 递归// 对外接口执行整个 AI 回合 executeTurn(): Promisevoid { return new Promise((resolve) { const enemyUnits this.gameEngine.getGameState().units .filter(u u.faction Faction.ENEMY u.isAlive !u.hasActed); if (enemyUnits.length 0) { resolve(); // 没有可行动单位立即结束 return; } this.executeUnitActions(enemyUnits, 0, resolve); }); } // 逐个单位行动每次间隔800ms private executeUnitActions(units: Unit[], index: number, resolve: () void): void { if (index units.length) { resolve(); // 所有单位行动完毕回调结束 return; } const unit units[index]; if (!unit.isAlive || unit.hasActed) { // 跳过死亡或已行动的单位 this.executeUnitActions(units, index 1, resolve); return; } this.executeUnitAction(unit); // 执行行动 // 800ms 后处理下一个单位 setTimeout(() { this.executeUnitActions(units, index 1, resolve); }, 800); }为什么用递归而不是循环// ❌ 错误同步循环所有行动瞬间完成for (const unit of units) {this.executeUnitAction(unit);}同步循环会导致所有 AI 行动在一帧内完成玩家看不到 AI 的决策过程。// ✅ 正确递归 setTimeout实现可观察的延迟序列this.executeUnitActions(units, index 1, resolve); // 在 setTimeout 回调中调用setTimeout 递归 是实现「带延迟的序列执行」的经典模式避免了 async/await 在某些 HarmonyOS 版本中的兼容性问题。Promise 封装的意义executeTurn() 返回 Promisevoid使得调用方可以用 .then() 在 AI 回合**真正结束后**执行后续操作// GamePage.etsthis.aiEngine.executeTurn().then(() {// AI 回合结束后切回玩家回合this.gameEngine.endTurn();this.gameState this.gameEngine.getGameState();this.isAITurn false;this.gameMessage 我方回合 - 请选择单位;this.drawGameMap();this.checkGameEnd();});如果不用 Promise就无法知道 AI 回合何时真正结束因为有异步延迟。四、单个单位行动决策private executeUnitAction(unit: Unit): void {// 优先级1能直接攻击const attackTarget this.findBestAttackTarget(unit);if (attackTarget) {this.gameEngine.attackUnit(unit, attackTarget);return; // 攻击后结束hasActed 由 attackUnit 设为 true}// 优先级2移动 尝试攻击const moveTarget this.findBestMoveTarget(unit);if (moveTarget) {this.gameEngine.moveUnit(unit, moveTarget);const newAttackTarget this.findBestAttackTarget(unit); // 移动后重新检查if (newAttackTarget) {this.gameEngine.attackUnit(unit, newAttackTarget);} else {unit.hasActed true; // 无法攻击单纯移动后结束}} else {unit.hasActed true; // 无法移动跳过}}优先级设计的合理性1. 能直接攻击时绝不浪费机会攻击 移动2. 不能攻击时移动靠近敌人移动后尝试攻击3. 无法移动时被困也会标记已行动防止死循环五、目标评估系统private evaluateTargetValue(attacker: Unit, target: Unit): number {let value 0;// 规则1优先攻击将军最高价值目标if (target.type 4) { // UnitType.GENERALvalue 100;}// 规则2优先攻击血量低的单位可能击杀const hpPercent target.hp / target.maxHp;if (hpPercent 0.3) {value 50; // 快击杀了}// 规则3攻击威胁高的单位攻击力×0.5value target.attack * 0.5;// 规则4远程克近战弓箭手/法师优先打战士/骑兵if (attacker.type 1 || attacker.type 3) { // ARCHER or MAGEif (target.type 0 || target.type 2) { // WARRIOR or CAVALRYvalue 20;}}// 规则5距离惩罚越近越好——减少需要追的步骤const distance Math.abs(attacker.position.x - target.position.x) Math.abs(attacker.position.y - target.position.y);value - distance * 2;return value;}评估函数的权重分析| 规则 | 加分 | 设计意图 ||------|------|---------|| 攻击将军 | 100 | 极高优先级将军是决胜关键 || 低血量目标 | 50 | 补刀意识减少敌方战斗力 || 高攻击力目标 | 0~17.5 | 消灭威胁法师35×0.517.5|| 远程克近战 | 20 | 体现兵种相克关系 || 距离惩罚 | -2/格 | 优先打近处目标 |权重的优先级保证将军加分 100 远大于其他加分之和最多约 87.5确保只要将军在攻击范围内AI 一定会优先攻击将军。这让游戏 AI 有了明确的策略目标感。六、移动位置评估系统private evaluateMovePosition(unit: Unit, pos: Position, enemyUnits: Unit[]): number {let value 0;// 计算到最近玩家单位的距离let minDistanceToEnemy 100;for (const enemy of enemyUnits) {const distance Math.abs(pos.x - enemy.position.x) Math.abs(pos.y - enemy.position.y);minDistanceToEnemy Math.min(minDistanceToEnemy, distance);}// 近战单位攻击范围1越近越好if (unit.attackRange 1) {value (10 - minDistanceToEnemy) * 10;}// 远程单位进入攻击范围但保持距离if (unit.attackRange 1) {if (minDistanceToEnemy unit.attackRange minDistanceToEnemy 1) {value 50; // 理想在攻击范围内且保持距离} else if (minDistanceToEnemy unit.attackRange) {value (unit.attackRange - minDistanceToEnemy 10) * 5; // 需要继续靠近}}// 有利地形加分const terrain this.gameEngine.getTerrainAt(pos);if (terrain 1) value 10; // 森林 10else if (terrain 2) value 15; // 山地 15else if (terrain 3) value - 100; // 水域 -100强力阻止// 低血量单位更保守优先站地形 远离敌人const hpPercent unit.hp / unit.maxHp;if (hpPercent 0.3) {if (terrain 1 || terrain 2) value 30; // 更愿意守地形value minDistanceToEnemy * 3; // 远离敌人}return value;}近战 vs 远程的移动逻辑// 近战单位战士、骑兵、将军// 距离越近越好距离1格加90分距离5格加50分距离10格加0分value (10 - minDistanceToEnemy) * 10;// 远程单位弓箭手、法师// 理想位置在攻击范围内distance ≤ attackRange但不紧贴distance 1if (minDistanceToEnemy unit.attackRange minDistanceToEnemy 1) {value 50; // 完美射击位置}远程单位的「保持距离」逻辑体现了战术思考弓箭手不应该冲到前线被近战单位反杀。低血量退缩逻辑if (hpPercent 0.3) {if (terrain 1 || terrain 2) value 30; // 更喜欢地形庇护value minDistanceToEnemy * 3; // 越远越好}血量低于 30% 时AI 会转入「自保模式」优先寻找防御地形并试图远离敌人。这让游戏 AI 有了「知道逃跑」的基本自我保护意识。七、AI 设计模式策略模式的体现evaluateTargetValue 和 evaluateMovePosition 本质上是**评估函数Evaluation Function**这是 AI 游戏设计中的核心概念评估函数 Σ(各项因素 × 权重)通过调整权重可以控制 AI 的「性格」- 增加攻击性权重 → AI 更激进- 增加防守地形权重 → AI 更保守- 增加将军权重 → AI 更专注斩首这种设计非常利于调参和扩展是战棋游戏 AI 的常见架构。八、AI 的局限性与改进方向当前 AI 是**单步贪心策略**只考虑「当前最优行动」不做长远规划。**改进方向**1. **Minimax 搜索**向前看 N 步选择最小化对方最大优势的方案2. **蒙特卡洛树搜索MCTS**随机模拟多次战斗统计获胜路径3. **A* 寻路**计算到达目标位置的最优路径而不只是「移动到最近的可达格」4. **群体协作**多个 AI 单位协同包围敌人而不是单独行动5. **规则引擎**设置更复杂的战术规则如保护将军、集火策略