# UE4如何定制AI的移动寻路方式

如何定制AI的移动寻路方式

初始化寻路过滤器

const FSharedConstNavQueryFilter& ATestMonsterAIController::GetOrCreateNavmeshQueryFilter()
{
    UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
    if (NavSys != nullptr)
    {
        const ANavigationData* NavData = NavSys->GetNavDataForProps(GetNavAgentPropertiesRef(), GetNavAgentLocation());
        if (NavData)
        {
            this->QueryFilter = NavData->GetDefaultQueryFilter()->GetCopy();
            if (FNavigationQueryFilter* DataNavFilter = const_cast<FNavigationQueryFilter*>(this->QueryFilter.Get()))
            {
                for (uint8 i = 0; i < RECAST_MAX_AREAS; ++i)
                {
                    DataNavFilter->SetExcludedArea(i);
                }
            }
        }
    }
    return this->QueryFilter;
}

设置寻路成本

//  指定区域不能走
void ATestMonsterAIController::AddExcludedArea(TSubclassOf<UNavArea> AreaClass)
{
    if (!this->QueryFilter.IsValid())
    {
        GetOrCreateNavmeshQueryFilter();
    }
    if (!this->QueryFilter.IsValid())
    {
        return;
    }
    UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
    const ANavigationData* NavData = (NavSys == nullptr) ? nullptr : NavSys->GetNavDataForProps(GetNavAgentPropertiesRef(), GetNavAgentLocation());
    if (!NavData)
    {
        return;
    }
    FSharedConstNavQueryFilter&amp; NavFilter = this->QueryFilter;

    if (FNavigationQueryFilter* DataNavFilter = const_cast<FNavigationQueryFilter*>(NavFilter.Get()))
    {
        DataNavFilter->SetExcludedArea(NavData->GetAreaID(AreaClass));
    }
}
// 指定哪些区域的移动成本
void ATestMonsterAIController::SetAreaCost(TSubclassOf<UNavArea> AreaClass, float Cost)
{
    if (!this->QueryFilter.IsValid())
    {
        GetOrCreateNavmeshQueryFilter();
    }
    if (!this->QueryFilter.IsValid())
    {
        return;
    }
    UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
    const ANavigationData* NavData = (NavSys == nullptr) ? nullptr : NavSys->GetNavDataForProps(GetNavAgentPropertiesRef(), GetNavAgentLocation());
    if (!NavData)
    {
        return;
    }
    FSharedConstNavQueryFilter&amp; NavFilter = this->QueryFilter;
    if (FNavigationQueryFilter* DataNavFilter = const_cast<FNavigationQueryFilter*>(NavFilter.Get()))
    {
        DataNavFilter->SetAreaCost(NavData->GetAreaID(AreaClass), Cost);
    }
}

运行时修改寻路过滤器

UE4的AIControoler寻路的时候调用顺序:
MoveToLocation/MoveToActor → 构建 FAIMoveRequest
MoveTo → 验证请求,检查是否已到达
BuildPathfindingQuery → 构建寻路查询对象
FindPathForMoveRequest ⭐ → 执行寻路(主要重写点)
RequestMove → 开始路径跟随
PathFollowingComponent::TickComponent → 每帧更新移动
OnMoveCompleted → 移动完成回调
最佳实践:
✅ 重写 FindPathForMoveRequest 来应用自定义 QueryFilter
✅ 在构造函数中初始化 QueryFilter
✅ 使用 SetAreaCost 和 AddExcludedArea 配置导航行为
✅ 调用 Super::FindPathForMoveRequest 执行实际寻路

sequenceDiagram
    participant BT as BTTask_MoveTo/Blueprint
    participant AIC as AAIController
    participant NavSys as UNavigationSystemV1
    participant PFC as UPathFollowingComponent

    Note over BT,PFC: 1. 发起移动请求
    BT->>AIC: MoveToLocation() / MoveToActor()
    Note right of AIC: 构建 FAIMoveRequest

    Note over BT,PFC: 2. 执行移动
    AIC->>AIC: MoveTo(FAIMoveRequest)
    Note right of AIC: 验证请求有效性<br/>检查是否已到达目标

    Note over BT,PFC: 3. 构建寻路查询
    AIC->>AIC: BuildPathfindingQuery()
    Note right of AIC: 获取导航数据<br/>设置起点/终点<br/>应用导航过滤器
    AIC->>PFC: OnPathfindingQuery(Query)
    Note right of PFC: 允许组件修改查询参数

    Note over BT,PFC: 4. 执行寻路 (可重写)
    AIC->>AIC: FindPathForMoveRequest()
    Note right of AIC: ⭐ 虚函数,可重写<br/>应用自定义 QueryFilter
    AIC->>NavSys: FindPathSync(Query)
    NavSys-->>AIC: FPathFindingResult
    Note right of AIC: 设置目标Actor观察<br/>启用路径重算

    Note over BT,PFC: 5. 请求移动执行
    AIC->>AIC: RequestMove(MoveRequest, Path)
    AIC->>PFC: RequestMove(MoveRequest, Path)
    Note right of PFC: 中止之前的移动<br/>存储路径和请求<br/>注册路径观察者<br/>开始路径跟随
    PFC-->>AIC: FAIRequestID
    AIC-->>BT: FPathFollowingRequestResult

    Note over BT,PFC: 6. 路径跟随
    loop 每帧更新
        PFC->>PFC: TickComponent()
        PFC->>PFC: UpdatePathSegment()
        Note right of PFC: 检查是否到达<br/>更新移动方向
    end

    Note over BT,PFC: 7. 移动完成
    PFC->>AIC: OnMoveCompleted()
    AIC->>BT: OnRequestFinished
sequenceDiagram
    participant BT as 行为树任务
    participant AIC as AIController
    participant NavSys as NavigationSystem
    participant NavData as NavigationData
    participant Path as PathFollowingComponent

    BT->>AIC: MoveTo/请求移动
    AIC->>AIC: FindPathForMoveRequest()
    Note over AIC: 1. 设置自定义QueryFilter<br>2. 准备FPathFindingQuery

    AIC->>NavSys: RequestPath()
    NavSys->>NavData: FindPath()
    NavData->>NavData: 执行寻路算法
    NavData-->>NavSys: 返回路径结果
    NavSys-->>AIC: 返回FNavPathSharedPtr

    AIC->>Path: 开始路径跟随
    Path->>Path: 逐帧移动更新
void ATestMonsterAIController::FindPathForMoveRequest(const FAIMoveRequest&amp; MoveRequest, FPathFindingQuery&amp; Query,
    FNavPathSharedPtr&amp; OutPath) const
{
    if (this->QueryFilter.IsValid())
    {
        // 发起寻路,就用自己身上定制的cost来处理寻路成本
        Query.QueryFilter = this->QueryFilter;
    }

    Super::FindPathForMoveRequest(MoveRequest, Query, OutPath);
}