Skip to content

目标过滤系统 ​

概述 ​

目标过滤系统是 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; } = [];
}

判定规则:目标必须 同时满足 以下两个条件才能通过:

  1. 拥有 Required 中的 所有 标签
  2. 不拥有 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 定义技能的合法目标过滤条件。它在以下场景中参与判定:

  1. SmartAcquire(自动选取):施法指令未指定目标时,系统在范围内搜索并选择第一个通过过滤的单位。搜索范围可由 AcquireRange 覆盖,目标优先级由 AcquireSorting 决定。
  2. 单位目标技能(AbilityTargetType.Unit):即使手动指定了目标,目标也必须通过 TargetingFilters 校验,否则指令被拒绝(CannotTargetThat)。
  3. 交互技能(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()、AI ScanFilters、技能 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

相关文档 ​