Skip to content

AI 系统 ​

调试搜索、接近和施法距离时,参见距离来源与运行诊断。该指南区分默认值与可配置覆盖,并说明如何读取实际节点结果。

概述 ​

AI 系统负责管理 NPC(非玩家角色)的智能行为,仅在服务端运行(#if SERVER)。

系统由两个层次组成:

> 术语说明 > > - TacticalAI 是 GameDataUnit 上的配置字段名,用来引用一个 GameDataAIThinkTree > - AIThinkTree 是运行时真正挂在单个单位上的战斗 AI 组件 > - 下文会同时使用 TacticalAI(配置入口) 和 AIThinkTree(运行时实例) 这两个名字,它们描述的是同一层“单体战斗 AI”

  • AIThinkTree — 单体 AI:为单个 Unit 提供行为树驱动的战斗决策(扫描目标、攻击、施法、移动)。每 15 帧(约 0.5 秒)思考一次。
  • WaveAI — 群体 AI:管理一组 Entity 的非战斗协同行为(守卫、巡逻、跟随)。每 60 帧(约 2 秒)思考一次。

两者协作方式:WaveAI 负责宏观移动(群体向目标行进、沿路线巡逻),AIThinkTree 负责战斗决策(扫描敌人、发动攻击、施放技能)。当 AIThinkTree 进入战斗状态时,WaveAI 会自动让出控制权。

> 一眼判断 > > - 想解决“单位会不会打、怎么选目标、怎么施法” → 查 TacticalAI / AIThinkTree > - 想解决“单位往哪走、跟随谁、何时接近目标” → 查 WaveAI > - 现象是“怪物会巡逻或追人,但不攻击” → 先查单位身上是否真的挂了 AIThinkTree(CreateUnit(..., useDefaultAI)、AIThinkTree.AddDefaultAI(unit)、myAIThinkTreeLink.Data!.CreateAI(unit)),再查 WaveAI.EnableCombat / SetCombatEnabled(...),最后才查 ScanRange、MinimalApproachRange、HysteresisFactor(Follow)/ ReacquireRangeFactor(Hunt)

架构 ​

GameDataAIThinkTree (数编配置)
  ├── ScanFilters          → 敌人扫描过滤条件
  ├── ScanSorts            → Scan 方法和未覆盖排序的扫描节点使用的标签优先级
  └── CombatBehaviorTree   → 行为树根节点 (GameDataAINode)

GameDataWaveAI (数编配置基类 = 守卫模式)
  ├── GameDataWaveAIFollow   → 跟随模式
  ├── GameDataWaveAIPatrol   → 巡逻模式
  └── GameDataWaveAIHunt     → 追杀模式

AIThinkTree (运行时, 每 Unit 一个)
  ├── CombatState 状态机   → OutOfCombat / InCombat / Leashing / Retreating
  ├── 行为树执行           → 战斗中每 tick 执行 CombatBehaviorTree
  └── 方法: Scan, AttackTarget, MoveTo, Cast 等

WaveAI (运行时, 管理一组 Entity)
  ├── 四种行为: Guard / Follow / Patrol / Hunt
  ├── 路线管理: SetRoute, ClearRoute, ResumeRouteFromNearest
  ├── 追杀管理: SetHuntTargets, ClearHuntTargets
  └── 事件: OnRoutePointReached, OnRouteCompleted, OnWaveEliminated

GameDataAIThinkTree 配置 ​

GameDataAIThinkTree 定义单个单位的战斗 AI 配置:

属性类型默认值说明
ScanFiltersTargetFilterComplex攻击敌方 Unit/Structure扫描目标的过滤条件。多条 TargetFilter 之间是 OR 逻辑。详见 目标过滤系统
ScanSortsList<UnitFilter>?null默认单位标签优先级,供 AIThinkTree.Scan() 和 TargetSorts 未配置的扫描节点使用;本身不比较距离或血量
CombatBehaviorTreeIGameLink<GameDataAINode>?null进入战斗后执行的行为树根节点

ScanSorts 是树级默认排序,行为树扫描节点的 TargetSorts 是节点级覆盖:

  • TargetSorts = null 或未设置:扫描节点回退到 ScanSorts,与 AIThinkTree.Scan() 一致。例如 [Hero, Unit] 先比较 Hero 标签,再比较 Unit 标签;全部相同时保留搜索结果顺序。标签列表为空或未配置时不主动排序。
  • TargetSorts = []:显式禁用该节点排序,保留搜索结果顺序,不回退到标签排序。
  • TargetSorts 配置了比较函数:完全由这些函数决定优先级,前一项返回 0 才继续下一项;全部返回 0 时保留搜索结果顺序,框架不再追加 ScanSorts 或距离比较。函数可以自行读取 thinkTree.Cache.ScanSorts,决定是否使用标签、以及标签和距离等条件的先后顺序。
  • 排序不会排除候选目标,“正前方优先”不等于“只允许正前方目标”。同优先级只保证遵循这一次搜索返回的顺序,不保证不同帧的搜索顺序一致。
  • 战斗外扫描只判断是否有敌人以进入战斗,不使用上述排序。扫描节点启用 RetainTarget 且旧目标仍合法时,会直接保留目标,直到需要重新选敌才排序。

GameSparkCore 的默认战斗树与战斗思考模板均显式配置 Validator 公式 默认战斗目标排序。公式先读取 thinkTree.Cache.ScanSorts,按标签优先级比较,再比较到思考单位的二维中心距离。默认树没有标签优先级时选择最近目标;模板的 [Hero, Unit] 则先选英雄,再在同优先级目标中选最近的。这是默认公式自己的选择,不是框架隐式追加的规则。修改或替换公式即可改变策略。

例如自定义“正前方优先、再英雄优先、最后最近”时,应由比较函数明确表达这三个层次;如果距离比较放在标签比较之前,标签就只在距离相同时生效。

默认 ScanFilters 配置:

csharp
ScanFilters = [
    new()
    {
        Required = [UnitRelationship.Enemy, UnitFilter.Unit, UnitRelationship.Visible],
        Excluded = [UnitFilter.Item, UnitState.Invulnerable]
    },
    new()
    {
        Required = [UnitRelationship.Enemy, UnitFilter.Structure, UnitRelationship.Visible],
        Excluded = [UnitState.Invulnerable]
    }
]

UnitRelationship.Visible 表示 AI 只会扫描施法者当前可见的敌人。精确视野或扇形视野玩法中,如果敌方怪物也必须受迷雾、墙体遮挡或自身扇形朝向限制,应保留这个条件,并让对应怪物成为合适的视野观察者。

如果项目是“玩家单位少、电脑怪物很多”,且电脑怪物不需要遵守迷雾/墙体/扇形,只需要按扫描半径发现周围敌人,可以自定义怪物 ScanFilters 去掉 UnitRelationship.Visible,从而不必让大量怪物都参与精确视野计算。此时潜行、不可被普通怪发现等玩法需要改由 UnitState 过滤表达,并同步检查技能的 AcquireSettings.TargetingFilters 和效果搜索的 GameDataEffectSearch.SearchFilters。详见 场景视野系统。

创建 AIThinkTree ​

csharp
// 方法 1:通过 Unit 数编配置的 TacticalAI 字段自动创建(推荐)
AIThinkTree.AddDefaultAI(unit);

// 方法 2:用数编 Link 手动创建,会替换已有的 AI
var ai = myAIThinkTreeLink.Data!.CreateAI(unit);

每个 Entity 只能有一个 AIThinkTree(独占组件)。重复创建会先销毁旧的。

行为树节点 ​

所有节点继承自 GameDataAINode,通过数编配置。节点的 Think(AIThinkTree) 返回 CmdResult(CmdResult.Ok 表示成功)。

组合节点 ​

节点类型逻辑说明
GameDataAINodeSequenceAND依次执行 Nodes,全部成功才成功,任一失败立即停止
GameDataAINodeSelectOR依次尝试 Nodes,任一成功立即返回,全部失败才失败
GameDataAINodeParallel策略执行所有 Nodes,按 SuccessPolicy 和 FailurePolicy 决定结果

组合节点的子节点属性名都是 Nodes(类型 List<IGameLink<GameDataAINode>?>)。

Parallel 节点策略 ​

ParallelPolicy 枚举:

  • RequireOne — 任一子节点满足条件即可
  • RequireAll — 所有子节点都必须满足条件
属性默认值说明
SuccessPolicyRequireOne成功条件
FailurePolicyRequireAll失败条件

验证节点(Validate) ​

验证节点继承 GameDataAINodeValidate,执行流程:先调用 Validators(可选前置条件),通过后执行 Actions。

许多验证节点的属性是 Func<AIThinkTree, T> 委托,在代码中设置(不是简单数值)。未设置时使用 AIThinkTree 的默认值。

节点类型说明代码属性
GameDataAINodeValidateScan扫描范围内的敌人并设为 ScanTargetThisTickRange: Func<float>?, IgnoreLeash: bool, TargetSorts: ContextualPriorityComparer?
GameDataAINodeValidateFocus设置/清除 FocusTarget(持续关注的目标)Target: Func<Entity?>?
GameDataAINodeValidateAttack对目标发起普攻(超出射程自动接近)Target: Func<Entity?>?
GameDataAINodeValidateAttackOrMoveTo尝试攻击,无法攻击则移动到目标Target: Func<Entity?>?
GameDataAINodeValidateMoveTo移动到目标实体附近Target: Func<Entity?>?, Range: Func<float>?
GameDataAINodeValidateMoveToPoint移动到固定坐标点Target: Func<ScenePoint?>?
GameDataAINodeValidateCast施放技能(超出距离自动接近)Ability: IGameLink?, Target: Func<Entity?>?, IsAcquired: bool, DoRecast: bool
GameDataAINodeCustom自定义逻辑Func: Func<AIThinkTree, CmdResult>

目标回退规则:当 Target 委托为 null 时,ValidateAttack / ValidateFocus / ValidateMoveTo / ValidateCast 都回退到 thinkTree.DefaultTarget(即 FocusTarget ?? ScanTargetThisTick)。

ValidateCast 自动选择技能 ​

GameDataAINodeValidateCast.Ability 为 null 时,会自动从单位的 AbilityManager 中筛选所有可用的非隐藏主动技能,随机选择一个施放。

行为树示例 ​

典型的 AI 战斗行为树结构:

Sequence (根节点)
├── ValidateScan        → 扫描敌人
├── ValidateFocus       → 锁定目标
└── Select              → 选择行动
    ├── ValidateCast    → 尝试施法
    └── ValidateAttackOrMoveTo → 攻击或移动

CombatState 状态机 ​

AIThinkTree 维护四种战斗状态:

OutOfCombat ──[扫描到敌人]──→ InCombat
     ↑                           │
     │                           ├──[超出 CombatResetRange]──→ Retreating
     │                           │                                │
     │                           └──[CombatDelay 到期]──→ Leashing ──[扫描到敌人]──→ InCombat
     │                                                       │
     └───────────────[回到重置点附近]─────────────────────────┘
                                                              │
     └───────────────[回到重置点附近]──────────────── Retreating
状态说明行为
OutOfCombat非战斗执行 CombatScanThink(扫描敌人)+ LeaderLeashThink(跟随领队)
InCombat战斗中执行行为树 CombatBehaviorTree;检查是否超出 CombatResetRange;施法中不中断
Leashing脱战拉回向重置点移动,但仍可因扫描到敌人重新进入战斗
Retreating强制撤退向重置点移动,不会重新进入战斗

关键机制:

  • 重置点(RetreatPoint):进入战斗时记录当前位置作为回撤锚点
  • CombatDelay:战斗最少持续 InCombatMinimalDuration(默认 2 秒),到期后进入 Leashing
  • CombatLeash:战斗中扫描目标时,超出此距离的目标不会被锁定
  • CombatResetRange:距离重置点/领队超过此值时强制撤退

GameDataWaveAI 配置 ​

WaveAI 配置使用类继承体系,基类 GameDataWaveAI 直接使用时表示守卫模式:

基类属性(所有模式共享) ​

属性类型默认值说明
EnableCombatboolfalse扫描到敌人时是否进入战斗
MinimalScanRangefloat500扫描最小范围
MaximalScanRangefloat1000扫描最大范围
MinimalApproachRangefloat200接近目标的最小距离
CombatLeashfloat1500战斗中不追击超出此距离的目标
CombatResetRangefloat1800超出此距离后强制返回重置点
WaveLeashfloat?null成员与领队的最大距离(栓绳)
EnableLinkedAggroboolfalse组内单位共享仇恨
InCombatMinimalDurationTimeSpan2 秒战斗最少持续时长
AutoDisposeOnEmptybooltrue所有单位移除后自动销毁 WaveAI

> ScanRange 计算规则:AIThinkTree 的实际扫描范围 = Clamp(攻击射程, MinimalScanRange, MaximalScanRange)。无攻击技能时使用 MinimalScanRange。 > > 战斗外 vs 战斗内扫描范围是分开的:上述公式只决定 AIThinkTree.ScanRange,即"战斗外"扫描 (OutOfCombatThink) 和 GameDataAINodeValidateScan 在 Range = null 时的回落值。一旦 CombatBehaviorTree 里的 Scan 节点显式给了 Range,"战斗内"扫描就以那个为准,与 AIThinkTree.ScanRange 完全独立。
> 历史上 GSC 的 Default ThinkTree 曾把 Scan.Range 覆盖为 AttackRange,导致目标距离在 (AttackRange, ScanRange] 区间时进战斗立刻丢目标 → ValidateCast 拿不到 DefaultTarget → AI 不接近也不攻击。自定义 CombatBehaviorTree 时建议 Scan 节点的 Range 保持 null,或显式设为 ≥ 期望追击距离的值。

守卫模式(GameDataWaveAI 基类) ​

单位守卫在 WaveTarget 附近。超出 WaveLeash(默认 500)时自动返回到 70% 的距离处。

跟随模式(GameDataWaveAIFollow) ​

单位持续跟随 WaveTarget 实体。使用滞后控制在 WaveAI/AIThinkTree 之间切换控制权:

属性类型默认值说明
HysteresisFactorfloat0.7切换阈值系数,防止在 MinimalApproachRange 边界震荡
MinControlDurationfloat1.5每个控制器最少持续时间(秒)

控制权切换逻辑:

  • 距目标 > MinimalApproachRange → WaveAI 控制(长距离移动),暂停 AIThinkTree
  • 距目标 < MinimalApproachRange * HysteresisFactor → AIThinkTree 控制(战斗决策),WaveAI 让出
  • 中间缓冲区 → 保持当前控制器不变

巡逻模式(GameDataWaveAIPatrol) ​

单位沿路线移动,支持三种循环模式:

属性类型默认值说明
RoutePlacedLineReference?null引用场景编辑器中放置的路线
PatrolModePatrolModeLoop循环模式
RouteArrivalRangefloat100判定到达的距离
RouteWaitDurationfloat0每个路径点停留时间(秒)
RouteSpacingfloat0单位横向间距(0 = 自动按 CollisionRadius × 2.5)
RouteCompletionConfigIGameLink<GameDataWaveAI>?nullOnce 模式走完后切换到的新配置

PatrolMode 枚举:

  • Loop — 循环:A→B→C→A→B→C…
  • PingPong — 往返:A→B→C→B→A→B→C…
  • Once — 单次:走完后停止,触发 OnRouteCompleted,可通过 RouteCompletionConfig 自动切换行为

追杀模式(GameDataWaveAIHunt) ​

每个成员独立追杀距自己最近的敌方目标,怪群自然分散。适用于生存类游戏中怪物不断涌向玩家的场景。

属性类型默认值说明
ReacquireRangeFactorfloat0.7外侧缓冲系数:AIThinkTree 接管后,目标距离 > MinimalApproachRange / 本值 才重新交回 WaveAI
MinControlDurationfloat1.5每个控制器最少持续时间(秒)
HuntFiltersTargetFilterComplexUnit, 排除 Invulnerable从敌方 Player.Units 中筛选有效追杀目标

目标选择逻辑:

  • 默认(自动模式):遍历 Player.AllPlayers → 筛选敌方玩家 → 遍历 Player.Units → 用 HuntFilters 过滤 → 同场景检查 → 取距该成员最近的目标
  • 显式覆盖:调用 SetHuntTargets(...) 指定固定目标列表,跳过自动遍历;调用 ClearHuntTargets() 回退到自动模式

与 Follow 模式的区别:

  • Follow:所有成员追向同一个 WaveTarget(一对多);MinimalApproachRange = 跟随保持的最小间距
  • Hunt:每个成员独立追向距自己最近的敌方目标(多对多);MinimalApproachRange = 进入交战的距离

控制权切换:WaveAI 把单位带到 MinimalApproachRange 处停下,然后唤醒 AIThinkTree 接管战斗(由 AIThinkTree 自行根据 AttackRange 决定是否继续靠近)。滞后机制在外侧:目标跑到 MinimalApproachRange / ReacquireRangeFactor(默认 ≈ +43%)以外,WaveAI 才会重新接管追击。

> 关于 MinimalApproachRange 在 Hunt 下的语义:它是"AIThinkTree 接管的距离",不是"攻击距离"。AIThinkTree 接管后会用自己的 ScanRange 搜敌(默认 = Math.Clamp(AttackRange, MinimalScanRange, MaximalScanRange),下界即本 WaveAI 上配置的 MinimalScanRange,默认 500),再自行根据 AttackRange 走到攻击位置。 > > 因此调参时: > > - MinimalApproachRange 应 ≤ MinimalScanRange(默认 500)—— 否则 AIThinkTree 接管时刚好扫不到目标,可能站桩。 > - 无需 MinimalApproachRange ≥ AttackRange —— AIThinkTree 接管后自己会贴近到攻击位置。 > - 注意 Sight 是单位视野(战争迷雾用),跟 AI 搜敌范围无关。

GameSparkCore 内置预设 ​

如果项目引用了 GameSparkCore,可以直接使用其内置的 WaveAI 预设:

预设类型说明
gamesparkcore.ScopeData.GameDataWaveAI.默认跟随AIGameDataWaveAIFollow常见跟随场景的默认配置
gamesparkcore.ScopeData.GameDataWaveAI.默认巡逻AIGameDataWaveAIPatrol常见巡逻/行军场景的默认配置
gamesparkcore.ScopeData.GameDataWaveAI.默认追杀AIGameDataWaveAIHunt近战生存/刷怪场景的默认追杀配置

其中 默认追杀AI 的目标是避免常见近战怪追杀死区,适合作为 starter preset,而不是覆盖所有 Boss、远程怪或大地图玩法的万能默认值。

GameSparkCore 还提供了便捷扩展方法:

csharp
#if SERVER
using gamesparkcore.AISystem;

// 自动搜索最近敌方目标并主动攻击
var waveAI = newEnemies.HuntAndAttack();

// 覆盖搜敌范围
var waveAI2 = newEnemies.HuntAndAttack(scanRange: 2400);

// 显式指定追杀目标,但不主动攻击
var waveAI3 = newEnemies.HuntWithoutAttack([hero, companion], scanRange: 1800);
#endif

这些扩展方法会使用 gamesparkcore.ScopeData.GameDataWaveAI.默认追杀AI 创建 WaveAI,并根据重载自动设置搜敌范围、目标列表和战斗开关。

使用方法 ​

创建守卫 AI ​

csharp
#if SERVER
// 1. 在数编中创建 GameDataWaveAI 和 GameDataAIThinkTree 配置
// 2. 创建 WaveAI 实例
var waveAI = myWaveAILink.Data!.CreateWaveAI();

// 3. 添加单位
waveAI.Add(unit1);
waveAI.Add(unit2);

// 4. 设置守卫目标
waveAI.WaveTarget = guardPoint;  // ITarget(Entity 或 ScenePoint)

// 5. 启动思考
waveAI.StartThinking();
#endif

创建巡逻 AI ​

csharp
#if SERVER
// 快捷方式:一步到位
var waveAI = myPatrolLink.Data!.StartWaveAI(
    units: [unit1, unit2, unit3],
    enableCombat: true,
    routePoints: [new Vector2(100, 100), new Vector2(500, 100), new Vector2(500, 500)]
);

// 或者手动步骤
var waveAI = myPatrolLink.Data!.CreateWaveAI();
foreach (var unit in units) waveAI.Add(unit);
waveAI.SetRoute(new Vector2(100, 100), new Vector2(500, 500));
waveAI.StartThinking();
#endif

创建跟随 AI ​

csharp
#if SERVER
var waveAI = myFollowLink.Data!.CreateWaveAI();
waveAI.Add(bodyguard1);
waveAI.Add(bodyguard2);
waveAI.WaveTarget = heroUnit;  // 跟随目标
waveAI.StartThinking();
#endif

创建追杀 AI ​

csharp
#if SERVER
// 方式1:自动追杀所有敌方单位(推荐,适合生存类游戏)
var waveAI = myHuntLink.Data!.CreateWaveAI();
foreach (var monster in spawnedMonsters) waveAI.Add(monster);
waveAI.StartThinking();
// 无需其他设置,自动遍历敌方 Player.Units 选择最近目标

// 方式2:显式指定追杀目标
waveAI.SetHuntTargets(player1.Unit, player2.Unit);

// 动态更新
waveAI.ClearHuntTargets();  // 回退到自动模式
#endif

为单位添加战斗 AI ​

csharp
#if SERVER
// 通过 Unit 数编配置的 TacticalAI 字段自动创建
AIThinkTree.AddDefaultAI(unit);
#endif

运行时控制 ​

AIThinkTree 启用/禁用 ​

csharp
var ai = entity.GetComponent<AIThinkTree>();

ai.Disable();   // 用户控制禁用
ai.Enable();    // 用户控制启用

ai.IsEnabled;        // 是否生效(综合所有禁用原因)
ai.IsUserDisabled;   // 是否被用户禁用
ai.IsFollowDisabled; // 是否被 Follow 模式暂停
ai.IsDeathDisabled;  // 是否因死亡禁用

三种禁用原因相互独立,只有全部解除才会恢复思考。

WaveAI 运行时控制 ​

csharp
// 动态切换战斗开关
waveAI.SetCombatEnabled(false);   // 禁用战斗(行军不战斗)
waveAI.SetCombatEnabled(null);    // 恢复为数编默认值

// 切换到新配置(如从 Patrol 切换到 Guard)
waveAI.SwitchConfig(guardConfigLink);

// 路线管理
waveAI.SetRoute(new Vector2(0, 0), new Vector2(1000, 1000));
waveAI.SetRoute(scene, placedLineId);       // 从场景中的 PlacedLine 设置
waveAI.ClearRoute();                        // 清空路线
waveAI.ResumeRouteFromNearest();            // 脱战后从最近路径点恢复
waveAI.AppendRoutePoint(new Vector2(2000, 0)); // 追加路径点
waveAI.ReverseRoute();                      // 反转方向

// 追杀目标管理(Hunt 模式)
waveAI.SetHuntTargets(player1.Unit, player2.Unit);  // 显式指定目标
waveAI.ClearHuntTargets();                           // 回退到自动模式

// 管理成员
waveAI.Add(newUnit);
waveAI.Remove(unit);

// 控制思考
waveAI.StopThinking();
waveAI.StartThinking();

WaveAI 事件 ​

事件签名触发时机
OnRoutePointReachedAction<WaveAI, int>到达路线上的某个路径点(参数为路径点索引)
OnRouteCompletedAction<WaveAI>PatrolMode.Once 走完整条路线
OnWaveEliminatedAction<WaveAI>所有单位被移除(在 AutoDisposeOnEmpty 之前触发)
csharp
waveAI.OnRouteCompleted += (wave) =>
{
    Game.Logger.LogInformation("巡逻完成,切换到守卫模式");
};

waveAI.OnWaveEliminated += (wave) =>
{
    Game.Logger.LogInformation("波次全灭");
};

AIThinkTree 辅助方法 ​

以下方法可在自定义行为树节点或 GameDataAINodeCustom.Func 中使用:

方法说明
Scan(rangeOverride?, ignoreLeash?)扫描敌人,结果存入 ScanTargetThisTick,支持 ScanSorts 排序
AttackTarget(target)发起普攻指令
MoveTo(target, range?)移动到目标实体附近
MoveToPosition(position)移动到坐标点
AttackOrMoveTo(target)尝试攻击,无法攻击则移动
Cast(abilityLink, target, isAcquired?, doRecast?)施放技能
CanCast(abilityLink, target)检查能否施放技能(不实际执行)
IsCasting()是否正在施法(Preswing 及之后阶段)
ClearUpAIOrders(includeUnInterruptible?)清除当前所有 AI 指令
属性说明
FocusTarget持续关注的目标,整个战斗期间保持
ScanTargetThisTick本次思考扫描到的目标,每 tick 重置
DefaultTargetFocusTarget ?? ScanTargetThisTick
ScanRange当前扫描范围 = Clamp(攻击射程, MinimalScanRange, MaximalScanRange)
DefaultApproachRange默认接近距离 = 攻击射程或 MinimalApproachRange
Host宿主 Entity
WaveAI所属的 WaveAI(可能为 null)
CombatState当前战斗状态
RetreatPoint回撤锚点

调试工具(DEBUG 构建) ​

#if SERVER && DEBUG 条件下可用。通过 AIDebugger 静态类访问:

csharp
// 开始调试某个 AI
var session = AIDebugger.StartDebugging(aiThinkTree, AIDebugLevel.Detailed);

// 设置条件断点
session.SetBreakpoint(ai => ai.CombatState == CombatState.InCombat, "进入战斗");

// 暂停 / 恢复 / 单步
session.Pause();
session.Resume();
session.EnableStepMode();
session.StepExecute();

// 查看决策历史和状态变化
var decisions = session.GetDecisionHistory(count: 10);
var stateChanges = session.GetStateHistory();

// 生成报告
var report = AIDebugger.GenerateDebugReport();

// 停止调试
AIDebugger.StopDebugging(entityId);

相关文档 ​