UE4自定义NavLink实现指南

UE4 自定义 NavLink 实现指南

概述

本文档详细介绍了如何在 UE4 中实现自定义的 NavLink 系统,包括自定义 NavLink 组件、路径跟随组件和 NavLinkProxy 的完整实现方案。该系统主要用于 Test 功能,支持 AI 在导航过程中与游戏对象(如门)进行交互。

系统架构

自定义 NavLink 系统由三个核心组件构成:

  1. UNavLinkTestCustomComponent – 自定义 NavLink 组件
  2. UTestPathFollowingComponent – 自定义路径跟随组件
  3. ATestNavLinkProxy – NavLink 代理 Actor

架构图

┌─────────────────────────────────────┐
│  ATestMonsterAIController           │
│  (AI 控制器)                         │
│  - 使用自定义 PathFollowingComponent │
└──────────────┬──────────────────────┘
               │
               │ 使用
               ▼
┌─────────────────────────────────────┐
│  UTestPathFollowingComponent        │
│  (路径跟随组件)                      │
│  - NavLink 前停顿                    │
│  - 触发 NavLink 事件                 │
└──────────────┬──────────────────────┘
               │
               │ 交互
               ▼
┌─────────────────────────────────────┐
│  ATestNavLinkProxy                  │
│  (NavLink 代理)                      │
│  - 包含自定义 NavLink 组件           │
└──────────────┬──────────────────────┘
               │
               │ 包含
               ▼
┌─────────────────────────────────────┐
│  UNavLinkTestCustomComponent        │
│  (自定义 NavLink 组件)               │
│  - 控制通行权限                      │
│  - 触发门开关                        │
└─────────────────────────────────────┘

组件详解

1. UNavLinkTestCustomComponent

自定义 NavLink 组件继承自 UNavLinkCustomComponent,提供了扩展的导航链接功能。

主要特性

  • 通行权限控制:支持分别控制怪物和玩家的通行权限
  • GM 指令支持:通过控制台变量动态控制 NavLink 的启用状态
  • GPO Actor 关联:可以关联游戏对象(如门),在 AI 通过时触发交互
  • 蓝图扩展:支持通过蓝图实现自定义的寻路权限判断

头文件定义

UCLASS(ClassGroup = (Navigation), meta = (BlueprintSpawnableComponent))
class UNavLinkTestCustomComponent : public UNavLinkCustomComponent, public IUnLuaInterface
{
    GENERATED_BODY()

public:
    UNavLinkTestCustomComponent(const FObjectInitializer& ObjectInitializer);

    // 蓝图可实现的寻路权限判断
    UFUNCTION(BlueprintImplementableEvent, Category = "Test|NavLink")
    bool IsLinkPathfindingAllowedBP(const UObject* Querier) const;

protected:
    // 重写父类虚函数
    virtual bool IsLinkPathfindingAllowed(const UObject* Querier) const override;
    virtual bool OnLinkMoveStarted(UObject* PathComp, const FVector& DestPoint) override;
    virtual void OnLinkMoveFinished(UObject* PathComp) override;

public:
    // 通行权限控制
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Test|NavLink")
    bool bAllowMonsterPass;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Test|NavLink")
    bool bAllowPlayerPass;

    // GPO Actor 管理
    UFUNCTION(BlueprintCallable, Category = "Test|NavLink")
    void SetRelativeGPOActor(AActor* InGPOActor);

    UFUNCTION(BlueprintCallable, Category = "Test|NavLink")
    ATestInteractiveActorBase* GetRelativeGPOActor() const;

protected:
    TWeakObjectPtr<ATestInteractiveActorBase> RelativeGPOActor;
};

核心实现

1. 寻路权限判断
bool UNavLinkTestCustomComponent::IsLinkPathfindingAllowed(const UObject* Querier) const
{
    // 检查是否是怪物 AI 控制器
    const ATestMonsterAIController* AIController = Cast<ATestMonsterAIController>(Querier);
    if (AIController)
    {
        // 首先检查 GM 指令(控制台变量)
        const int32 bGMAllowMonster = CVarTestNavLinkAllowMonster.GetValueOnGameThread();
        if (bGMAllowMonster == 0)
        {
            return false;
        }

        // 然后检查组件自身的属性
        return bAllowMonsterPass;
    }

    // 检查玩家权限
    const int32 bGMAllowPlayer = CVarTestNavLinkAllowPlayer.GetValueOnGameThread();
    if (bGMAllowPlayer == 0)
    {
        return false;
    }

    return bAllowPlayerPass &amp;&amp; Super::IsLinkPathfindingAllowed(Querier);
}
2. NavLink 移动事件处理
bool UNavLinkTestCustomComponent::OnLinkMoveStarted(UObject* PathComp, const FVector&amp; DestPoint)
{
    // 当 AI 开始使用 NavLink 时,打开关联的门
    if (RelativeGPOActor.IsValid())
    {
        auto PathFollowCmp = Cast<UTestPathFollowingComponent>(PathComp);
        if (PathFollowCmp)
        {
            RelativeGPOActor->OpenByAI(PathFollowCmp->GetOwner());
        }
    }
    return Super::OnLinkMoveStarted(PathComp, DestPoint);
}

void UNavLinkTestCustomComponent::OnLinkMoveFinished(UObject* PathComp)
{
    // 当 AI 完成 NavLink 移动时,关闭关联的门
    if (RelativeGPOActor.IsValid())
    {
        auto PathFollowCmp = Cast<UTestPathFollowingComponent>(PathComp);
        if (PathFollowCmp)
        {
            RelativeGPOActor->CloseByAI(PathFollowCmp->GetOwner());
        }
    }
    Super::OnLinkMoveFinished(PathComp);
}

GM 指令

系统提供了两个控制台变量用于调试:

// 控制怪物能否使用 NavLink
Test.NavLink.AllowMonster 0/1

// 控制玩家能否使用 NavLink
Test.NavLink.AllowPlayer 0/1

2. UTestPathFollowingComponent

自定义路径跟随组件继承自 UPathFollowingComponent,主要功能是在 AI 使用 NavLink 前添加停顿时间。

主要特性

  • NavLink 前停顿:在使用 NavLink 前停顿指定时间(例如等待门打开)
  • 定时器管理:使用定时器控制停顿时间
  • 状态缓存:缓存 NavLink 信息,停顿结束后继续移动

头文件定义

UCLASS()
class FEATURE_Test_API UTestPathFollowingComponent : public UPathFollowingComponent
{
    GENERATED_BODY()

public:
    UTestPathFollowingComponent(const FObjectInitializer&amp; ObjectInitializer);

    /** 设置 NavLink 停顿时间 */
    void SetNavLinkPauseTime(float InPauseTime);

protected:
    /** NavLink 停顿时间(秒) */
    float NavLinkPauseTime;

    /** 是否正在等待 NavLink 停顿 */
    bool bWaitingForNavLinkPause;

    /** NavLink 停顿计时器句柄 */
    FTimerHandle NavLinkPauseTimerHandle;

    /** 缓存的 NavLink 目标点 */
    FVector CachedNavLinkDestPoint;

    /** 缓存的 NavLink 接口 */
    TWeakInterfacePtr<INavLinkCustomInterface> CachedCustomNavLink;

    /** 重写 StartUsingCustomLink,在使用 NavLink 前停顿 */
    virtual void StartUsingCustomLink(INavLinkCustomInterface* CustomNavLink, const FVector&amp; DestPoint) override;

    /** NavLink 停顿结束后继续移动 */
    void OnNavLinkPauseFinished();

    /** 重写 UpdatePathSegment,处理 NavLink 停顿 */
    virtual void UpdatePathSegment() override;
    virtual void FollowPathSegment(float DeltaTime) override;

    /** 清理定时器 */
    virtual void Reset() override;
};

核心实现

1. 开始使用 NavLink
void UTestPathFollowingComponent::StartUsingCustomLink(INavLinkCustomInterface* CustomNavLink, const FVector&amp; DestPoint)
{
    // 如果设置了停顿时间,则先停顿
    if (NavLinkPauseTime > 0.0f &amp;&amp; !bWaitingForNavLinkPause)
    {
        UE_VLOG(GetOwner(), LogPathFollowing, Log, 
                TEXT("PVE PathFollowing: Pausing %.2f seconds before using NavLink"), 
                NavLinkPauseTime);

        // 缓存 NavLink 信息
        CachedCustomNavLink = CustomNavLink;
        CachedNavLinkDestPoint = DestPoint;
        bWaitingForNavLinkPause = true;

        // 触发 NavLink 开始事件
        CustomNavLink->OnLinkMoveStarted(this, DestPoint);

        // 暂停移动
        if (MovementComp)
        {
            MovementComp->StopMovementKeepPathing();
        }

        // 设置定时器,停顿结束后继续
        if (AAIController* AIOwner = Cast<AAIController>(GetOwner()))
        {
            AIOwner->GetWorldTimerManager().SetTimer(
                NavLinkPauseTimerHandle,
                this,
                &amp;UTestPathFollowingComponent::OnNavLinkPauseFinished,
                NavLinkPauseTime,
                false
            );
        }
    }
    else
    {
        // 没有设置停顿时间,直接调用父类方法
        Super::StartUsingCustomLink(CustomNavLink, DestPoint);
    }
}
2. 停顿结束处理
void UTestPathFollowingComponent::OnNavLinkPauseFinished()
{
    UE_VLOG(GetOwner(), LogPathFollowing, Log, 
            TEXT("PVE PathFollowing: NavLink pause finished, continuing move"));

    bWaitingForNavLinkPause = false;
    NavLinkPauseTimerHandle.Invalidate();

    // 继续使用 NavLink
    if (CachedCustomNavLink.IsValid())
    {
        INavLinkCustomInterface* CustomNavLink = CachedCustomNavLink.GetInterface();
        Super::StartUsingCustomLink(CustomNavLink, CachedNavLinkDestPoint);

        // 清理缓存
        CachedCustomNavLink = TWeakInterfacePtr<INavLinkCustomInterface>();
        CachedNavLinkDestPoint = FVector::ZeroVector;
    }
}
3. 路径更新控制
void UTestPathFollowingComponent::UpdatePathSegment()
{
    // 如果正在等待 NavLink 停顿,不更新路径段
    if (bWaitingForNavLinkPause)
    {
        return;
    }
    Super::UpdatePathSegment();
}

void UTestPathFollowingComponent::FollowPathSegment(float DeltaTime)
{
    // 如果正在等待 NavLink 停顿,不更新路径段
    if (bWaitingForNavLinkPause)
    {
        return;
    }
    Super::FollowPathSegment(DeltaTime);
}
4. 资源清理
void UTestPathFollowingComponent::Reset()
{
    // 清理定时器
    if (NavLinkPauseTimerHandle.IsValid())
    {
        if (AAIController* AIOwner = Cast<AAIController>(GetOwner()))
        {
            AIOwner->GetWorldTimerManager().ClearTimer(NavLinkPauseTimerHandle);
        }
        NavLinkPauseTimerHandle.Invalidate();
    }

    bWaitingForNavLinkPause = false;
    CachedCustomNavLink = TWeakInterfacePtr<INavLinkCustomInterface>();
    CachedNavLinkDestPoint = FVector::ZeroVector;

    Super::Reset();
}

3. ATestNavLinkProxy

NavLink 代理 Actor 继承自 ANavLinkProxy,用于在关卡中放置和配置 NavLink。

主要特性

  • 自定义组件替换:通过 ObjectInitializer 替换默认的 UNavLinkCustomComponent
  • 动态配置:支持运行时设置 NavLink 的起点、终点和方向
  • GPO Actor 关联:支持关联游戏对象并根据距离选择最近的对象

头文件定义

UCLASS(Blueprintable, autoCollapseCategories = (SmartLink, Actor), hideCategories = (Input))
class ATestNavLinkProxy : public ANavLinkProxy, public IUnLuaInterface
{
    GENERATED_BODY()

public:
    ATestNavLinkProxy(const FObjectInitializer&amp; ObjectInitializer);

    // 设置 NavLink 数据
    UFUNCTION(BlueprintCallable, Category = "Test")
    void SetupSmartLinkData(const FVector&amp; Start, const FVector&amp; End, ENavLinkDirection::Type Direction);

    // 获取起点和终点
    UFUNCTION(BlueprintCallable, Category = "Test")
    FVector GetStartPoint();

    UFUNCTION(BlueprintCallable, Category = "Test")
    FVector GetEndPoint();

    // GPO Actor 管理
    UFUNCTION(BlueprintCallable, Category = "Test")
    void SetRelativeGPOActor(AActor* InGPOActor, bool Force = false);

    UFUNCTION(BlueprintCallable, Category = "Test")
    AActor* GetRelativeGPOActor() const;

    // 通行权限控制
    UFUNCTION(BlueprintCallable, Category = "Test")
    bool IsAllowMonsterPass() const;

    UFUNCTION(BlueprintCallable, Category = "Test")
    void SetAllowMonsterPass(bool bInAllowMonsterPass);
};

核心实现

1. 构造函数 – 替换默认组件
ATestNavLinkProxy::ATestNavLinkProxy(const FObjectInitializer&amp; ObjectInitializer)
    : Super(ObjectInitializer.SetDefaultSubobjectClass<UNavLinkTestCustomComponent>(TEXT("SmartLinkComp")))
{
    // 构造函数中通过 ObjectInitializer 替换默认的 SmartLinkComp 为自定义组件
    // 这样 GetSmartLinkComp() 返回的就是 UNavLinkTestCustomComponent 实例
    UNavLinkCustomComponent* SmartLinkComponent = GetSmartLinkComp();
    if (!SmartLinkComponent)
    {
        return;
    }

    uint32 UserId = SmartLinkComponent->GetLinkId();
    if (PointLinks.Num() > 0)
    {
        PointLinks[0].UserId = SmartLinkComponent->GetLinkId();
    }
}
2. 设置 NavLink 数据
void ATestNavLinkProxy::SetupSmartLinkData(const FVector&amp; Start, const FVector&amp; End, ENavLinkDirection::Type Direction)
{
    UNavLinkCustomComponent* SmartLinkComponent = GetSmartLinkComp();
    if (!SmartLinkComponent)
    {
        return;
    }

    uint32 UserId = SmartLinkComponent->GetLinkId();
    if (UserId == 0)
    {
        UE_LOG(LogTemp, Error, TEXT("TestNavLinkProxy's UserId is 0!!!"));
    }

    // 设置 PointLinks 数据
    if (PointLinks.Num() > 0)
    {
        PointLinks[0].Left = Start;
        PointLinks[0].Right = End;
        PointLinks[0].Direction = Direction;
        PointLinks[0].UserId = SmartLinkComponent->GetLinkId();
    }

    // 设置组件数据
    if (SmartLinkComponent)
    {
        SmartLinkComponent->SetLinkData(Start, End, Direction);
        SmartLinkComponent->SetNavigationRelevancy(true);
    }
}
3. GPO Actor 关联(智能距离选择)
void ATestNavLinkProxy::SetRelativeGPOActor(AActor* InGPOActor, bool Force)
{
    UNavLinkTestCustomComponent* SmartLinkComponent = Cast<UNavLinkTestCustomComponent>(GetSmartLinkComp());
    if (SmartLinkComponent)
    {
        if (Force)
        {
            SmartLinkComponent->SetRelativeGPOActor(InGPOActor);
            return;
        }

        // 如果已经设置了 GPOActor,比较距离,选择更近的
        auto RelativeGPOActor = SmartLinkComponent->GetRelativeGPOActor();
        if (RelativeGPOActor)
        {
            float Distance = (RelativeGPOActor->GetActorLocation() - this->GetActorLocation()).Size();
            float NewDistance = (InGPOActor->GetActorLocation() - this->GetActorLocation()).Size();
            if (NewDistance < Distance)
            {
                SmartLinkComponent->SetRelativeGPOActor(InGPOActor);
            }
            return;
        }

        SmartLinkComponent->SetRelativeGPOActor(InGPOActor);
    }
}

4. AI 控制器集成

在 AI 控制器中使用自定义路径跟随组件:

UCLASS()
class FEATURE_Test_API ATestMonsterAIController : public AMoeMonsterAIController
{
    GENERATED_BODY()

public:
    ATestMonsterAIController(const FObjectInitializer&amp; ObjectInitializer);
};

// 实现文件
ATestMonsterAIController::ATestMonsterAIController(const FObjectInitializer&amp; ObjectInitializer)
    : Super(ObjectInitializer.SetDefaultSubobjectClass<UTestPathFollowingComponent>(TEXT("PathFollowingComponent")))
{
    // 通过 ObjectInitializer 替换默认的 PathFollowingComponent
}

使用流程

1. 在关卡中放置 NavLinkProxy

// 在蓝图或 C++ 中创建 TestNavLinkProxy
ATestNavLinkProxy* NavLinkProxy = World->SpawnActor<ATestNavLinkProxy>(Location, Rotation);

// 设置 NavLink 的起点和终点
FVector StartPoint = FVector(0, 0, 0);
FVector EndPoint = FVector(0, 0, 100);
NavLinkProxy->SetupSmartLinkData(StartPoint, EndPoint, ENavLinkDirection::BothWays);

// 关联门对象
NavLinkProxy->SetRelativeGPOActor(DoorActor);

// 设置通行权限
NavLinkProxy->SetAllowMonsterPass(true);

2. 配置 AI 控制器

// AI 控制器会自动使用 UTestPathFollowingComponent
ATestMonsterAIController* AIController = Cast<ATestMonsterAIController>(Monster->GetController());

// 可选:设置 NavLink 停顿时间
if (UTestPathFollowingComponent* PathFollowComp = Cast<UTestPathFollowingComponent>(AIController->GetPathFollowingComponent()))
{
    PathFollowComp->SetNavLinkPauseTime(3.0f); // 停顿 3 秒
}

3. AI 寻路

// 正常使用 AI 寻路
AIController->MoveToLocation(TargetLocation);

// 当 AI 遇到 NavLink 时:
// 1. PathFollowingComponent 检测到 NavLink
// 2. 调用 NavLinkComponent->IsLinkPathfindingAllowed() 检查权限
// 3. 如果允许,调用 StartUsingCustomLink()
// 4. NavLinkComponent->OnLinkMoveStarted() 被调用,触发门打开
// 5. PathFollowingComponent 停顿指定时间
// 6. 停顿结束后,AI 继续移动通过 NavLink
// 7. NavLinkComponent->OnLinkMoveFinished() 被调用,触发门关闭

工作流程图

┌─────────────────────┐
│  AI 开始寻路         │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────┐
│  检测到 NavLink      │
└──────────┬──────────┘
           │
           ▼
┌─────────────────────────────────┐
│  IsLinkPathfindingAllowed()     │
│  - 检查 GM 指令                  │
│  - 检查通行权限                  │
└──────────┬──────────────────────┘
           │
           ▼ (允许通过)
┌─────────────────────────────────┐
│  StartUsingCustomLink()         │
│  - 缓存 NavLink 信息             │
│  - 触发 OnLinkMoveStarted()      │
│  - 停止移动                      │
└──────────┬──────────────────────┘
           │
           ▼
┌─────────────────────────────────┐
│  OnLinkMoveStarted()            │
│  - 打开关联的门                  │
└──────────┬──────────────────────┘
           │
           ▼
┌─────────────────────────────────┐
│  等待停顿时间                    │
│  (定时器)                        │
└──────────┬──────────────────────┘
           │
           ▼
┌─────────────────────────────────┐
│  OnNavLinkPauseFinished()       │
│  - 继续使用 NavLink              │
└──────────┬──────────────────────┘
           │
           ▼
┌─────────────────────────────────┐
│  AI 移动通过 NavLink             │
└──────────┬──────────────────────┘
           │
           ▼
┌─────────────────────────────────┐
│  OnLinkMoveFinished()           │
│  - 关闭关联的门                  │
└──────────┬──────────────────────┘
           │
           ▼
┌─────────────────────┐
│  继续寻路            │
└─────────────────────┘

关键技术点

1. ObjectInitializer 替换默认组件

UE4 允许在构造函数中通过 ObjectInitializer 替换默认的子对象类:

// 在 AI 控制器中替换 PathFollowingComponent
ATestMonsterAIController::ATestMonsterAIController(const FObjectInitializer&amp; ObjectInitializer)
    : Super(ObjectInitializer.SetDefaultSubobjectClass<UTestPathFollowingComponent>(TEXT("PathFollowingComponent")))
{
}

// 在 NavLinkProxy 中替换 SmartLinkComp
ATestNavLinkProxy::ATestNavLinkProxy(const FObjectInitializer&amp; ObjectInitializer)
    : Super(ObjectInitializer.SetDefaultSubobjectClass<UNavLinkTestCustomComponent>(TEXT("SmartLinkComp")))
{
}

2. 弱指针避免循环引用

使用 TWeakObjectPtrTWeakInterfacePtr 避免循环引用:

// 存储 GPO Actor 的弱指针
TWeakObjectPtr<ATestInteractiveActorBase> RelativeGPOActor;

// 存储 NavLink 接口的弱指针
TWeakInterfacePtr<INavLinkCustomInterface> CachedCustomNavLink;

3. 定时器管理

使用 UE4 的定时器系统实现延迟执行:

// 设置定时器
AIOwner->GetWorldTimerManager().SetTimer(
    NavLinkPauseTimerHandle,
    this,
    &amp;UTestPathFollowingComponent::OnNavLinkPauseFinished,
    NavLinkPauseTime,
    false
);

// 清理定时器
AIOwner->GetWorldTimerManager().ClearTimer(NavLinkPauseTimerHandle);
NavLinkPauseTimerHandle.Invalidate();

4. 控制台变量(CVar)

使用控制台变量实现运行时调试:

static TAutoConsoleVariable<int32> CVarTestNavLinkAllowMonster(
    TEXT("Test.NavLink.AllowMonster"),
    1,
    TEXT("控制怪物 AI 能否使用 NavLink 进行寻路\n")
    TEXT("0: 禁止怪物使用 NavLink\n")
    TEXT("1: 允许怪物使用 NavLink (默认)"),
    ECVF_Cheat
);

// 使用
const int32 bGMAllowMonster = CVarTestNavLinkAllowMonster.GetValueOnGameThread();

调试技巧

1. 可视化日志

使用 UE4 的可视化日志系统:

UE_VLOG(GetOwner(), LogPathFollowing, Log, 
        TEXT("PVE PathFollowing: Pausing %.2f seconds before using NavLink"), 
        NavLinkPauseTime);

2. 控制台命令

// 禁止怪物使用 NavLink
Test.NavLink.AllowMonster 0

// 允许怪物使用 NavLink
Test.NavLink.AllowMonster 1

// 显示导航网格
show Navigation

3. 断点调试

关键断点位置:

  • IsLinkPathfindingAllowed() – 检查寻路权限
  • StartUsingCustomLink() – NavLink 开始使用
  • OnLinkMoveStarted() – NavLink 移动开始
  • OnNavLinkPauseFinished() – 停顿结束

最佳实践

1. 内存管理

  • 使用弱指针(TWeakObjectPtr)存储 Actor 引用,避免循环引用
  • Reset() 方法中清理所有定时器和缓存数据
  • 使用 IsValid() 检查弱指针的有效性

2. 性能优化

  • 避免在 IsLinkPathfindingAllowed() 中执行耗时操作
  • 使用控制台变量进行全局控制,减少单个 NavLink 的检查开销
  • 合理设置 NavLink 停顿时间,避免 AI 等待过久

3. 扩展性设计

  • 通过继承 UNavLinkCustomComponent 实现特定功能
  • 使用蓝图可实现事件(BlueprintImplementableEvent)提供扩展点
  • 支持 Lua 脚本扩展(通过 IUnLuaInterface

4. 错误处理

  • 检查组件指针的有效性
  • 验证 UserId 不为 0
  • 使用日志记录关键错误信息

常见问题

Q1: AI 不使用 NavLink

可能原因:

  • IsLinkPathfindingAllowed() 返回 false
  • GM 指令禁用了 NavLink
  • NavLink 的 UserId 为 0
  • NavLink 未正确注册到导航系统

解决方案:

  • 检查控制台变量设置
  • 验证 bAllowMonsterPass 属性
  • 确保调用了 SetNavigationRelevancy(true)

Q2: AI 在 NavLink 处卡住

可能原因:

  • 定时器未正确触发
  • bWaitingForNavLinkPause 状态未正确重置
  • NavLink 的终点位置不可达

解决方案:

  • 检查定时器是否正确设置和清理
  • Reset() 中确保状态重置
  • 验证 NavLink 的起点和终点位置

Q3: 门没有打开/关闭

可能原因:

  • GPO Actor 未正确关联
  • OnLinkMoveStarted/Finished() 未被调用
  • GPO Actor 的 OpenByAI/CloseByAI() 方法未实现

解决方案:

  • 使用 SetRelativeGPOActor() 正确关联门对象
  • 添加日志验证事件调用
  • 检查 GPO Actor 的实现

总结

本文档详细介绍了 UE4 自定义 NavLink 系统的完整实现方案,包括:

  1. 三个核心组件:NavLink 组件、路径跟随组件和 NavLinkProxy
  2. 关键技术:组件替换、弱指针、定时器、控制台变量
  3. 完整流程:从 AI 寻路到 NavLink 使用的完整工作流程
  4. 最佳实践:内存管理、性能优化、扩展性设计

通过这套系统,可以实现 AI 在导航过程中与游戏对象的智能交互,例如自动开门、等待障碍物移除等功能。系统设计灵活,易于扩展,适合在各种 PVE 场景中使用。

参考资料

  • UE4 官方文档:Navigation System
  • UE4 官方文档:AI Navigation
  • UE4 源码:NavLinkCustomComponent.h
  • UE4 源码:PathFollowingComponent.h