主题
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, OnWaveEliminatedGameDataAIThinkTree 配置
GameDataAIThinkTree 定义单个单位的战斗 AI 配置:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ScanFilters | TargetFilterComplex | 攻击敌方 Unit/Structure | 扫描目标的过滤条件。多条 TargetFilter 之间是 OR 逻辑。详见 目标过滤系统 |
ScanSorts | List<UnitFilter>? | null | 默认单位标签优先级,供 AIThinkTree.Scan() 和 TargetSorts 未配置的扫描节点使用;本身不比较距离或血量 |
CombatBehaviorTree | IGameLink<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 表示成功)。
组合节点
| 节点类型 | 逻辑 | 说明 |
|---|---|---|
GameDataAINodeSequence | AND | 依次执行 Nodes,全部成功才成功,任一失败立即停止 |
GameDataAINodeSelect | OR | 依次尝试 Nodes,任一成功立即返回,全部失败才失败 |
GameDataAINodeParallel | 策略 | 执行所有 Nodes,按 SuccessPolicy 和 FailurePolicy 决定结果 |
组合节点的子节点属性名都是 Nodes(类型 List<IGameLink<GameDataAINode>?>)。
Parallel 节点策略
ParallelPolicy 枚举:
RequireOne— 任一子节点满足条件即可RequireAll— 所有子节点都必须满足条件
| 属性 | 默认值 | 说明 |
|---|---|---|
SuccessPolicy | RequireOne | 成功条件 |
FailurePolicy | RequireAll | 失败条件 |
验证节点(Validate)
验证节点继承 GameDataAINodeValidate,执行流程:先调用 Validators(可选前置条件),通过后执行 Actions。
许多验证节点的属性是 Func<AIThinkTree, T> 委托,在代码中设置(不是简单数值)。未设置时使用 AIThinkTree 的默认值。
| 节点类型 | 说明 | 代码属性 |
|---|---|---|
GameDataAINodeValidateScan | 扫描范围内的敌人并设为 ScanTargetThisTick | Range: 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 直接使用时表示守卫模式:
基类属性(所有模式共享)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
EnableCombat | bool | false | 扫描到敌人时是否进入战斗 |
MinimalScanRange | float | 500 | 扫描最小范围 |
MaximalScanRange | float | 1000 | 扫描最大范围 |
MinimalApproachRange | float | 200 | 接近目标的最小距离 |
CombatLeash | float | 1500 | 战斗中不追击超出此距离的目标 |
CombatResetRange | float | 1800 | 超出此距离后强制返回重置点 |
WaveLeash | float? | null | 成员与领队的最大距离(栓绳) |
EnableLinkedAggro | bool | false | 组内单位共享仇恨 |
InCombatMinimalDuration | TimeSpan | 2 秒 | 战斗最少持续时长 |
AutoDisposeOnEmpty | bool | true | 所有单位移除后自动销毁 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 之间切换控制权:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
HysteresisFactor | float | 0.7 | 切换阈值系数,防止在 MinimalApproachRange 边界震荡 |
MinControlDuration | float | 1.5 | 每个控制器最少持续时间(秒) |
控制权切换逻辑:
- 距目标 >
MinimalApproachRange→ WaveAI 控制(长距离移动),暂停 AIThinkTree - 距目标 <
MinimalApproachRange * HysteresisFactor→ AIThinkTree 控制(战斗决策),WaveAI 让出 - 中间缓冲区 → 保持当前控制器不变
巡逻模式(GameDataWaveAIPatrol)
单位沿路线移动,支持三种循环模式:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Route | PlacedLineReference? | null | 引用场景编辑器中放置的路线 |
PatrolMode | PatrolMode | Loop | 循环模式 |
RouteArrivalRange | float | 100 | 判定到达的距离 |
RouteWaitDuration | float | 0 | 每个路径点停留时间(秒) |
RouteSpacing | float | 0 | 单位横向间距(0 = 自动按 CollisionRadius × 2.5) |
RouteCompletionConfig | IGameLink<GameDataWaveAI>? | null | Once 模式走完后切换到的新配置 |
PatrolMode 枚举:
Loop— 循环:A→B→C→A→B→C…PingPong— 往返:A→B→C→B→A→B→C…Once— 单次:走完后停止,触发OnRouteCompleted,可通过RouteCompletionConfig自动切换行为
追杀模式(GameDataWaveAIHunt)
每个成员独立追杀距自己最近的敌方目标,怪群自然分散。适用于生存类游戏中怪物不断涌向玩家的场景。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ReacquireRangeFactor | float | 0.7 | 外侧缓冲系数:AIThinkTree 接管后,目标距离 > MinimalApproachRange / 本值 才重新交回 WaveAI |
MinControlDuration | float | 1.5 | 每个控制器最少持续时间(秒) |
HuntFilters | TargetFilterComplex | Unit, 排除 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.默认跟随AI | GameDataWaveAIFollow | 常见跟随场景的默认配置 |
gamesparkcore.ScopeData.GameDataWaveAI.默认巡逻AI | GameDataWaveAIPatrol | 常见巡逻/行军场景的默认配置 |
gamesparkcore.ScopeData.GameDataWaveAI.默认追杀AI | GameDataWaveAIHunt | 近战生存/刷怪场景的默认追杀配置 |
其中 默认追杀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 事件
| 事件 | 签名 | 触发时机 |
|---|---|---|
OnRoutePointReached | Action<WaveAI, int> | 到达路线上的某个路径点(参数为路径点索引) |
OnRouteCompleted | Action<WaveAI> | PatrolMode.Once 走完整条路线 |
OnWaveEliminated | Action<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 重置 |
DefaultTarget | FocusTarget ?? 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);相关文档
- 目标过滤系统 — ScanFilters 的过滤逻辑和标签体系
- Thinker 系统 — AIThinkTree 和 WaveAI 使用的
IThinkerStaggered调度机制 - OrderSystem — AI 通过指令系统执行移动、攻击、施法
- AbilitySystem — 技能施放和冷却
- UnitSystem — Unit 的 TacticalAI 属性(数编配置 AIThinkTree Link)