主题
目标过滤系统
概述
目标过滤系统是 AI 目标扫描和技能目标选取的共享基础设施。它通过统一的标签机制和过滤规则,决定"谁可以被谁攻击/选中"。
核心类型:
| 类型 | 说明 |
|---|---|
UnitFilterTag | 可扩展枚举基类,所有标签的统一类型 |
TargetFilter | 单条过滤规则(AND 逻辑) |
TargetFilterComplex | 多条过滤规则的组合(OR 逻辑) |
UnitFilterTag 下辖三组标签,按性质分为:
| 标签组 | 性质 | 来源 |
|---|---|---|
UnitRelationship | 动态计算 | 施法者与目标的玩家阵营关系 |
UnitState | 动态状态 | 运行时由 Buff、系统等设置/清除 |
UnitFilter | 静态分类 | 在 GameDataUnit.Filter 中配置,运行时不变 |
三组标签共享同一个 UnitFilterTag 基类型,因此可以在同一个 TargetFilter 的 Required 或 Excluded 集合中混合使用。
UnitRelationship(关系标签)
关系标签是运行时根据施法者和目标所属玩家的阵营关系动态计算的。同一对单位,从不同施法者视角看到的关系标签不同。
| 值 | 含义 |
|---|---|
UnitRelationship.Self | 目标是施法者自身 |
UnitRelationship.Player | 目标属于施法者的同一玩家 |
UnitRelationship.Alliance | 目标属于施法者的盟友玩家 |
UnitRelationship.Neutral | 目标属于中立玩家 |
UnitRelationship.Enemy | 目标属于敌方玩家 |
UnitRelationship.Visible | 目标在施法者视野内可见 |
说明:
Self和Player不互斥——自身单位同时拥有Self、Player、Visible三个标签。因此Required = [UnitRelationship.Player]会匹配自身单位。若需排除自身,需显式添加Excluded = [UnitRelationship.Self]Visible取决于施法者对目标的可见性判定,涉及阵营和状态的综合计算:- 同阵营(
Player/Alliance)玩家默认共享视野,因此即使目标处于InvisibleToEnemy隐身状态,友方仍可见 - 敌方玩家受战争迷雾和隐身影响——
InvisibleToEnemy的目标对敌方不可见,除非被Detected状态压制 Hidden状态的单位对所有人不可见(包括友方)
- 同阵营(
- 对电脑 AI 来说,是否要求
Visible是玩法选择:要求它时,怪物只会攻击自己可见的目标;去掉它时,怪物可以按 AI 扫描范围发现目标,不再受视野/迷雾限制。大量怪物不需要遵守精确视野时,可以用这种方式减少精确视野观察者数量,但要用UnitState等过滤条件补上潜行、不可选中等玩法规则 - 阵营关系由
Player的Alliance/Enemy配置决定,参见 玩家系统
UnitState(状态标签)
状态标签是运行时可变的,通常由 Buff 系统或游戏逻辑动态设置和清除。
过滤中常用的状态标签:
| 值 | 含义 | 典型过滤用法 |
|---|---|---|
UnitState.Invulnerable | 无敌 | Excluded — 不攻击无敌单位 |
UnitState.Dead | 死亡 | Excluded — 不攻击已死亡单位 |
UnitState.Hidden | 隐藏(完全不可见) | Excluded — 不攻击隐藏单位 |
UnitState.InvisibleToEnemy | 对敌方不可见 | 与 Visible 标签联动 |
UnitState.SpellImmunity | 魔免 | Excluded — 魔法技能排除魔免目标 |
UnitState.Ethereal | 物免(虚无) | Excluded — 物理技能排除虚无目标 |
UnitState.IgnoreSelector | 无法被搜索 | 所有基于场景范围的搜索均忽略(含 AI、技能、脚本),仅可通过直接引用或 Player.Units 获取 |
UnitState 共有 24 个值,完整列表和行为语义详见 UnitState 系统。
UnitFilter(分类标签)
分类标签是静态的,在 GameDataUnit.Filter 属性中配置,创建后不可改变。所有参与战斗的单位必须配置 Filter,否则 AI 无法检测到该单位。
主分类(互相并列,不可叠加)
| 值 | 含义 | 标准 Filter 配置 |
|---|---|---|
UnitFilter.Unit | 可移动的战斗单位(英雄、小兵、怪物) | [UnitFilter.Unit] |
UnitFilter.Structure | 固定建筑(塔、基地) | [UnitFilter.Structure] |
UnitFilter.Missile | 投射物 | [UnitFilter.Missile] |
UnitFilter.Item | 物品 | [UnitFilter.Item] |
子分类(与主分类叠加)
| 值 | 含义 | 典型用法 |
|---|---|---|
UnitFilter.Hero | 英雄单位 | [UnitFilter.Unit, UnitFilter.Hero] |
UnitFilter.Ground | 地面单位 | 与 Unit 或 Structure 叠加 |
UnitFilter.Air | 空中单位 | 与 Unit 叠加 |
设计原则
- Unit 和 Structure 是并列的主分类,两者互不包含
- 默认 AI(
ScopeData.AI.Default)的ScanFilters使用 OR 逻辑同时匹配UnitFilter.Unit和UnitFilter.Structure,会自动攻击两种目标 - 子分类标签(Hero, Ground, Air)是叠加性质的,用于进一步细化过滤条件
Missile和Item通常不参与战斗过滤,多数 ScanFilters 会排除或不要求它们
过滤组合逻辑
TargetFilter(AND 语义)
单条过滤规则,包含两个标签集合:
csharp
public class TargetFilter
{
public HashSet<UnitFilterTag> Required { get; set; } = [];
public HashSet<UnitFilterTag> Excluded { get; set; } = [];
}判定规则:目标必须 同时满足 以下两个条件才能通过:
- 拥有
Required中的 所有 标签 - 不拥有
Excluded中的 任何 标签
csharp
// 示例:筛选"敌方可见战斗单位,排除无敌和物品"
new TargetFilter
{
Required = [UnitRelationship.Enemy, UnitFilter.Unit, UnitRelationship.Visible],
Excluded = [UnitState.Invulnerable, UnitFilter.Item]
}TargetFilterComplex(OR 语义)
多条 TargetFilter 的集合(继承 List<TargetFilter>),目标只需通过 任意一条 规则即可:
csharp
// 示例:攻击敌方战斗单位 OR 敌方建筑
new TargetFilterComplex
{
new TargetFilter
{
Required = [UnitRelationship.Enemy, UnitFilter.Unit, UnitRelationship.Visible],
Excluded = [UnitFilter.Item, UnitState.Invulnerable]
},
new TargetFilter
{
Required = [UnitRelationship.Enemy, UnitFilter.Structure, UnitRelationship.Visible],
Excluded = [UnitState.Invulnerable]
}
}特殊行为:当 TargetFilterComplex 为空集合(Count == 0)时,默认 放行所有目标。
组合逻辑图
TargetFilterComplex
├── TargetFilter[0] ── Required 全部满足 AND Excluded 全部不含 ──┐
├── TargetFilter[1] ── Required 全部满足 AND Excluded 全部不含 ──┤ OR → 通过
├── TargetFilter[2] ── Required 全部满足 AND Excluded 全部不含 ──┘
└── (空集合) ── 默认放行应用场景
AI 目标扫描(ScanFilters)
GameDataAIThinkTree.ScanFilters 控制 AI 扫描哪些目标作为攻击候选。详见 AI 系统。
csharp
public partial class GameDataAIThinkTree
{
public TargetFilterComplex ScanFilters { get; set; } = [
new()
{
Required = [UnitRelationship.Enemy, UnitFilter.Unit, UnitRelationship.Visible],
Excluded = [UnitFilter.Item, UnitState.Invulnerable]
},
new()
{
Required = [UnitRelationship.Enemy, UnitFilter.Structure, UnitRelationship.Visible],
Excluded = [UnitState.Invulnerable]
}
];
}框架默认 AI 同时搜索 Unit 和 Structure 两类目标。若需自定义 AI 只攻击特定类型:
csharp
// 只攻击英雄单位的 AI
ScanFilters = [new()
{
Required = [UnitRelationship.Enemy, UnitFilter.Unit, UnitFilter.Hero, UnitRelationship.Visible],
Excluded = [UnitState.Invulnerable, UnitState.Dead]
}]技能目标选取(TargetingFilters)
AcquireSettings.TargetingFilters 定义技能的合法目标过滤条件。它在以下场景中参与判定:
- SmartAcquire(自动选取):施法指令未指定目标时,系统在范围内搜索并选择第一个通过过滤的单位。搜索范围可由
AcquireRange覆盖,目标优先级由AcquireSorting决定。 - 单位目标技能(
AbilityTargetType.Unit):即使手动指定了目标,目标也必须通过TargetingFilters校验,否则指令被拒绝(CannotTargetThat)。 - 交互技能(
AbilityTargetType.Interact):施法关系反转——过滤的是借用技能的单位(下达指令者),而非技能持有者。
其他技能类型(Ground、UnitOrGround、Vector 等)的 TargetingFilters 仅影响 SmartAcquire 自动选取,不参与手动指定目标时的指令校验。
csharp
AcquireSettings = new()
{
TargetingFilters = [new()
{
Required = [UnitRelationship.Enemy],
Excluded = [UnitState.Dead, UnitState.Invulnerable],
}],
}当 TargetingFilters 为空时,不做任何过滤——包括施法者自己也是合法目标,可能导致英雄攻击自己。
> 注意区分指令层与执行层的过滤 > > TargetingFilters 属于指令层——控制"技能能否对某个目标下达施法指令"。而 AOE 技能实际命中哪些单位,由效果树中 GameDataEffectSearch.SearchFilters 控制(执行层)。两者独立,互不影响。 > > 例如一个点目标 AOE 技能(AbilityTargetType.Ground),TargetingFilters 不会过滤范围内被命中的单位——那是 GameDataEffectSearch.SearchFilters 的职责。
运行时手动过滤
TargetFilterComplex.Pass(Entity caster, Entity target) 可用于运行时判定目标是否满足过滤条件:
csharp
var filter = new TargetFilterComplex
{
new() { Required = [UnitRelationship.Enemy, UnitFilter.Unit] }
};
if (filter.Pass(caster, target))
{
// target 是 caster 的敌方战斗单位
}常用 API
关系判定
| API | 说明 |
|---|---|
entity.GetRelationship(target) | 返回 target 相对于 entity 的 UnitRelationship(含 Self 判定)。与 Player.GetRelationShip 不同,此方法能区分"同一实体"和"同一玩家的不同实体"。 |
csharp
if (myUnit.GetRelationship(other) == UnitRelationship.Enemy)
{
// other 是 myUnit 的敌人
}静态分类判定
| API | 说明 |
|---|---|
entity.HasFilter(UnitFilter) | 判定实体是否拥有指定的静态分类标签。 |
entity.Filters | 获取实体的静态分类标签集合(HashSet<UnitFilter>?)。Unit 返回 GameDataUnit.Filter 的缓存集合,Entity 基类返回 null。 |
csharp
if (target.HasFilter(UnitFilter.Structure))
{
// target 是建筑
}状态判定
| API | 说明 |
|---|---|
unit.HasState(UnitState) | 判定单位是否处于指定状态。 |
unit.GetStates() | 获取单位当前所有状态的集合。 |
unit.AddState(UnitState) | 添加状态(SERVER)。引用计数机制,见下文。 |
unit.RemoveState(UnitState) | 移除状态(SERVER)。引用计数 -1,降至 0 时状态才真正清除。 |
unit.AddImmunity(UnitState) | 添加状态免疫(SERVER)。免疫生效期间该状态被压制,不论引用计数。 |
unit.RemoveImmunity(UnitState) | 移除状态免疫(SERVER)。若引用计数仍 > 0,状态恢复生效。 |
unit.HasImmunity(UnitState) | 判定是否免疫指定状态(SERVER)。 |
引用计数机制:AddState 和 RemoveState 基于引用计数。两个 Buff 各 Add 一次同一状态,第一个 Buff 到期 Remove 后状态仍然存在(计数从 2 降为 1)。只有计数降至 0 时状态才真正清除。AddImmunity 同理——免疫计数 > 0 时状态被压制,即使 AddState 也无效。
过滤判定
| API | 说明 |
|---|---|
targetFilter.Pass(tags) | 标签集是否通过此过滤规则(AND 逻辑)。 |
targetFilter.Pass(caster, target) | 便利方法:自动提取标签并判定(内部调用 GetFilterTags)。 |
targetFilterComplex.Pass(tags) | 标签集是否通过任一过滤规则(OR 逻辑)。 |
targetFilterComplex.Pass(caster, target) | 便利方法:直接判定两个实体。 |
entity.GetFilterTags(caster, tags) | 填充完整标签集(含 Relationship + State + Filter),用于手动过滤。 |
csharp
var filter = new TargetFilter
{
Required = [UnitRelationship.Enemy, UnitFilter.Unit],
Excluded = [UnitState.Invulnerable]
};
if (filter.Pass(myUnit, target))
{
// target 是 myUnit 的敌方战斗单位且非无敌
}可扩展性
UnitFilterTag 基于 [EnumExtension] 机制,UnitState 和 UnitFilter 均标记为 Extendable = true,游戏项目可以定义新的状态和分类标签:
csharp
// 扩展静态分类标签
[EnumExtension(Extends = typeof(UnitFilter))]
internal enum ECustomUnitFilter
{
Boss,
Summon,
Ward,
}
// 生成 → CustomUnitFilter.Boss, CustomUnitFilter.Summon, ...
// 扩展动态状态标签
[EnumExtension(Extends = typeof(UnitState))]
internal enum ECustomUnitState
{
Stunned,
Silenced,
}
// 生成 → CustomUnitState.Stunned, CustomUnitState.Silenced, ...扩展 UnitFilter 或 UnitState(而非直接扩展基类 UnitFilterTag)的好处是与现有基础设施无缝集成:
- 数据编辑器:
GameDataUnit.Filter的下拉列表自动出现扩展的分类选项,无需额外配置 - 过滤 API:
TargetFilter.Pass()、AIScanFilters、技能TargetingFilters等现有判定逻辑直接支持扩展值,零额外代码 - 语义明确:扩展
UnitFilter表示新增静态分类,扩展UnitState表示新增动态状态,编辑器和 API 知道如何归类
详见 EnumExtension 工具。
常见问题
| 现象 | 原因 | 解决方案 |
|---|---|---|
| AI 不攻击敌人 | 目标单位的 Filter 缺少 ScanFilters.Required 中要求的标签(如 UnitFilter.Unit) | 在 GameDataUnit.Filter 中配置正确的分类标签 |
| 技能攻击自己 | AcquireSettings.TargetingFilters 未配置 | 添加 Required = [UnitRelationship.Enemy] |
| 技能伤害无敌单位 | TargetingFilters.Excluded 未包含 UnitState.Invulnerable | 在 Excluded 中加入 UnitState.Invulnerable |
| 技能伤害已死亡单位 | Excluded 未包含 UnitState.Dead | 在 Excluded 中加入 UnitState.Dead |
| AI 不攻击建筑 | ScanFilters 的 Required 只包含 UnitFilter.Unit,不匹配 Structure | 添加第二条 TargetFilter 规则包含 UnitFilter.Structure(OR 逻辑) |
| AOE 技能命中了不该打的单位 | 修改了 TargetingFilters 但实际控制 AOE 命中的是 GameDataEffectSearch.SearchFilters | 修改效果树中 GameDataEffectSearch 的 SearchFilters,而非技能的 TargetingFilters |
相关文档
- AI 系统 — AI 行为树和目标扫描
- 技能系统 — 技能配置和目标选取
- UnitState 系统 — 状态标签完整列表和行为语义
- 单位系统 —
GameDataUnit.Filter属性 - EnumExtension 工具 — 可扩展枚举机制
- 玩家系统 — 玩家阵营关系配置