如何在不使用If语句的情况下实现带优先级的A*寻路AI系统
基于性格的AI行为重构方案
核心思路:策略模式
通过策略模式将不同性格的AI行为逻辑解耦,避免大量嵌套if语句,同时方便后续扩展新的性格类型。核心是为每种性格定义独立的行为策略类,主AI类只需根据当前性格调用对应策略的方法。
步骤1:定义AI行为策略接口
创建统一接口规范所有AI行为的核心方法:
public interface IAIPersonalityStrategy { void HandleNormalState(EnemyAI enemy); void HandleLowHPState(EnemyAI enemy); }
步骤2:实现各性格的策略类
针对每种性格编写对应的行为逻辑:
骄傲型(Proud)AI策略
骄傲的敌人不会求援,优先回血,即使低血量也坚持战斗到极限:
public class ProudAIStrategy : IAIPersonalityStrategy { public void HandleNormalState(EnemyAI enemy) { // 正常状态下始终追击目标 enemy.destinationSetter.target = enemy.target; } public void HandleLowHPState(EnemyAI enemy) { // 低血量时优先寻找回血点,找不到才继续战斗 var healStation = enemy.ScanForHelp(enemy.whatIsHeal); enemy.destinationSetter.target = healStation ?? enemy.target; } }
聪明型(Smart)AI策略
聪明的敌人会优先求援,求援失败再找回血点,最大化生存概率:
public class SmartAIStrategy : IAIPersonalityStrategy { public void HandleNormalState(EnemyAI enemy) { enemy.destinationSetter.target = enemy.target; } public void HandleLowHPState(EnemyAI enemy) { if (enemy.canCallSupport) { var support = enemy.ScanForHelp(enemy.whatIsSupport); if (support != null) { enemy.destinationSetter.target = support; enemy.CallSupport(); return; } } // 求援失败或无法求援时,寻找回血点 var healStation = enemy.ScanForHelp(enemy.whatIsHeal); enemy.destinationSetter.target = healStation ?? enemy.target; } }
愚笨型(Dumb)AI策略
愚笨的敌人逻辑简单,低血量时随机选择求援或回血,甚至可能原地不动:
public class DumbAIStrategy : IAIPersonalityStrategy { private readonly System.Random _random = new System.Random(); public void HandleNormalState(EnemyAI enemy) { // 偶尔可能忘记追击(模拟愚笨) if (_random.Next(10) > 2) { enemy.destinationSetter.target = enemy.target; } } public void HandleLowHPState(EnemyAI enemy) { int choice = _random.Next(3); switch (choice) { case 0: // 求援 if (enemy.canCallSupport) { var support = enemy.ScanForHelp(enemy.whatIsSupport); enemy.destinationSetter.target = support ?? enemy.target; } break; case 1: // 回血 var healStation = enemy.ScanForHelp(enemy.whatIsHeal); enemy.destinationSetter.target = healStation ?? enemy.target; break; case 2: // 原地不动 enemy.destinationSetter.target = null; break; } } }
步骤3:重构EnemyAI主类
将原有的嵌套逻辑替换为策略调用,同时修复ScanForHelp的索引越界问题:
using UnityEngine; using Pathfinding; public class EnemyAI : MonoBehaviour { public enum Personality { Proud, Smart, Dumb } public Personality personality; public bool lowHP; public bool inDanger; public bool shouldMove; public bool canCallSupport = true; public Transform target; public Transform healStation; public Transform swarm; public float scanRadius; public float alertRadius; public LayerMask whatIsSupport; public LayerMask whatIsHeal; public AIDestinationSetter destinationSetter; private IAIPersonalityStrategy _currentStrategy; private void Awake() { destinationSetter = GetComponent<AIDestinationSetter>(); // 根据初始性格设置策略 SetPersonalityStrategy(personality); } private void Update() { if (!shouldMove) return; if (!lowHP) { _currentStrategy.HandleNormalState(this); } else { _currentStrategy.HandleLowHPState(this); } } // 切换性格策略的方法 public void SetPersonalityStrategy(Personality newPersonality) { personality = newPersonality; switch (newPersonality) { case Personality.Proud: _currentStrategy = new ProudAIStrategy(); break; case Personality.Smart: _currentStrategy = new SmartAIStrategy(); break; case Personality.Dumb: _currentStrategy = new DumbAIStrategy(); break; } } private void OnDrawGizmos() { Gizmos.color = Color.green; Gizmos.DrawWireSphere(transform.position, scanRadius); Gizmos.color = Color.red; Gizmos.DrawWireSphere(transform.position, alertRadius); } // 修复后的扫描辅助方法 public Transform ScanForHelp(LayerMask mask) { RaycastHit2D[] hits = Physics2D.CircleCastAll(transform.position, scanRadius, Vector2.right, 0.1f, mask); if (hits.Length == 0) return null; Transform closestHelp = hits[0].transform; float closestDistance = Vector3.Distance(transform.position, closestHelp.position); foreach (RaycastHit2D hit in hits) { float distance = Vector3.Distance(transform.position, hit.transform.position); if (distance < closestDistance) { closestDistance = distance; closestHelp = hit.transform; } } return closestHelp; } public void CallSupport() { RaycastHit2D[] hits = Physics2D.CircleCastAll(transform.position, alertRadius, Vector2.right, 0.1f, whatIsSupport); foreach (RaycastHit2D hit in hits) { if (hit.collider.TryGetComponent<AIDestinationSetter>(out var setter)) { setter.target = target; } if (hit.collider.TryGetComponent<AIPath>(out var path)) { path.canMove = true; } } lowHP = false; } }
方案优势
- 低耦合:每种性格的行为逻辑独立,修改或新增性格无需改动主AI类代码。
- 可读性强:策略类命名清晰,行为逻辑一目了然,避免嵌套
if的混乱。 - 扩展性高:新增性格只需实现
IAIPersonalityStrategy接口即可,符合开闭原则。
内容的提问来源于stack exchange,提问作者Marko
相关产品推荐
相关产品推荐

