AI寻路:NavLink 是并发协议,不是一段停顿时间

自动关联目录:AI寻路:NavLink 是并发协议,不是一段停顿时间

核心判断:门、跳跃点和窄通道上的 Smart NavLink 是多个 AI 竞争的共享资源。暂停几秒再继续只能解决单 AI 演出,不能解决并发占用、取消、关门和迟到回调。

一、业务冲突:门要有演出,寻路又不能被门卡死

AI 到门前需要停顿、开门、通过并按规则关门。路径查询还要按怪物类型设置 Area Cost 或禁行区;多人群聚时要局部避障。业务上看是一个“MoveTo”,引擎里却横跨 BT Task、UAITask_MoveTo、PathFollowing、Smart Link、门状态和 MovementComponent。

二、实际实现:已经下沉到 PathFollowing,但所有权分散

旧路径在切换 QueryFilter 时主动使当前路径失效,避免继续沿旧成本行走。新实现为每个 AIController 复制 QueryFilter,按 Area 设置成本,并把默认 PathFollowingComponent 替换为自定义组件。

BT Task 把门前停顿和通过时间写入 PathFollowing;组件缓存弱引用 NavLink 与目标点,调用 StopMovementKeepPathing,用两个 Timer 控制预停和结束,并在 Reset 中清理 Timer。NavLink 的可用性检查留在 C++ 布尔字段,没有进入脚本热路径,这一点正确。

sequenceDiagram
    participant A as Agent MoveRequest
    participant L as Link Arbiter
    participant D as Door
    A->>L: Acquire(LinkId, RequestId, Incarnation)
    L-->>A: Lease
    L->>D: first entrant opens door
    A->>L: BeginTraverse(Lease)
    alt complete
        A->>L: Release(Lease)
    else abort/repath/death
        A->>L: Cancel(Lease)
    end
    L->>D: last entrant closes door

三、值得保留:取消清理属于移动基础设施

TimerHandle 在 Reset 中统一清理、NavLink 使用弱引用、等待期间停止路径段更新,这些都比在行为树脚本里散落 Delay 更可靠。Area Cost 下沉到 QueryFilter,也让路径选择而非到达后碰撞来表达“可走但昂贵”。

四、结构性缺陷:同一个默认子对象无法同时拥有两套 PathFollowing

工程中另一种控制器用 UCrowdFollowingComponent 替换同名默认子对象以获得 Detour Crowd,而门逻辑又替换成自定义 UPathFollowingComponent。两者不是可叠加组件;若自定义类不继承 CrowdFollowing,门演出与 Crowd 避障只能二选一。

更具体的问题是 NavLink 只保存一个共享整数状态:第一个 AI 开门后,后续 AI 不再计数;任意一个 AI 完成就可能关门,哪怕还有别的 AI 正在通过。BT Task、AITask 和 PathFollowing 还各自保留停顿字段与默认值,部分回调没有实际绑定,谁拥有时序并不唯一。

五、目标设计:把 Link 变成带 Lease 的并发资源

移动请求生成单调 MoveRequestId,所有 Timer 和异步路径结果都捕获该 ID 与 Actor Incarnation。Link Arbiter 维护活跃 Lease 集合;0 -> 1 时开门,1 -> 0 时才允许关门。Abort、Repath、Stun、Death 和 Pool Return 必须走同一 Release 路径。Crowd 与门逻辑应合并到同一 PathFollowing 派生层,或明确用 CharacterMovement RVO 替代并记录取舍。

void OnLinkReached(MoveContext m, LinkHandle link)
{
    LinkLease lease = Links.TryAcquire(link, m.agent, m.requestId);
    if (!lease) return Repath();
    OwnedLease = lease;
    Schedule(m.prePause, [this, token = m.Token()] {
        if (!IsCurrent(token) || !OwnedLease.IsValid()) return;
        BeginCustomTraversal(OwnedLease);
    });
}

void AbortMove()
{
    CancelOwnedTimers();
    Links.Release(OwnedLease);
    OwnedLease = {};
}

六、决策规则:移动请求、路径和 Link 各有生命周期

对象唯一标识必须取消的时机
MoveRequestRequestId + Actor Incarnation新 MoveTo、Stun、Death、回池
PathPathId + NavRevision动态门变化、Filter 变化、Repath
Link LeaseLinkId + LeaseId完成、Abort、掉线、对象销毁
Door StateDoor Revision由 Lease 集合驱动,不由单个 AI 写死
Crowd AgentCrowd RegistrationIdController/Pawn 解绑、降级模拟
Timer/CallbackCaptured RequestId回调执行前校验当前请求

这段实现已经把 Smart Link 时序放到了正确的 UE 层级;真正的升级点是从“单 AI 的定时器”提升为“多 AI 的并发协议”,并解决 CrowdFollowing 与自定义 PathFollowing 的继承冲突。