六一的部落格


关关难过关关过,前路漫漫亦灿灿。




说明

AI Perception Component

允许NPC游戏角色感知给定范围内的世界中的其他Actor, 通过视觉和听觉感知敌人, 对伤害作出反应

本节为NPC添加视觉感知, 寻找与之最近的敌人, 使NPC面向该敌人


创建C++类

-
基类 AIPerceptionComponent
Components
Public
STUAIPerceptionComponent

为AIController添加感知组件

  • 添加构造函数

    ShootThemUp: AI/STUAIController.h

    public
    1ASTUAIController();
  • 添加组件

    ShootThemUp: AI/STUAIController.h

    protected
    1class USTUAIPerceptionComponent;
    2
    3UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
    4USTUAIPerceptionComponent *STUAIPerceptionComponent;
  • 初始化组件

    ShootThemUp: AI/STUAIController.cpp
    1#include "Components/STUAIPerceptionComponent.h"
    2
    3ASTUAIController::ASTUAIController()
    4{
    5    STUAIPerceptionComponent = CreateDefaultSubobject<USTUAIPerceptionComponent>("STUPerceptionComponent");
    6    SetPerceptionComponent(*STUAIPerceptionComponent);
    7}

设置感知组件

BP_STUAIController > STUAIPerceptionComponent > Details

AI Perception > Senses Config : 对游戏角色的感官进行配置, 可以感知的有伤害, 听觉, 视觉等


添加AI视觉


选择 AI Sight config


设置捕捉可视半径为1000 Sense > Sight Radius

设置监控释放半径为1500 Sense > Lose Sight Radius

设置可视半角为60 Sense > PeripheralVisionHalfAngleDegrees

设置调试颜色为蓝色 Sense > Debug Color

Sense > Detection by Affiliation , 勾选 Detect Enemies , Detect Neutrals , Detect Friendlies ; 当前没有分队别

设置MaxAge为1: Actor从视野中消失多久后不再保存相关信息



设置感官优先级

可以将 Sense > DominantSense 设置为None或 AISense_Sight



查看

  • 关闭行为树

    BP_STUCharactr

    按下 Option , 左键点击箭头


    UE4.27: 移除Root到Sequence的箭头即可

    UE5.1: 必须移除Sequence到子任务的箭头

  • 在新窗口运行游戏


  • 按下 ' 打开AI调试信息; 在小写键盘按下数字4, 使能 Perception 信息

  • 绿色圆柱体表示捕捉可视边界; 粉色圆柱体表示监控释放边界


  • 进入到捕捉可视区域, 游戏角色所处位置出现蓝色调试球体, 表明游戏角色被NPC捕捉到; 绿色可视角


  • 蓝色调试球体的活动范围由可视角和监控释放边界组成

    进入到捕捉可视区域, 被NPC捕捉, 之后只要在活动范围内, 其移动就会被NPC捕捉到

    活动范围内, 位于障碍物之后, MaxAge时间后, 蓝色调试球消失

    移动到活动范围外, MaxAge时间后, 蓝色调试球消失

  • 在场景中添加另一个NPC


场景中有多个游戏角色: NPC靠近和他距离最近的游戏角色


获取组件

GetComponentByClass也是AActor的成员函数: 修改传参类型

public

ShootThemUp: STUUtils.h

1// #include "GameFramework/Pawn.h"
2#include "GameFramework/Actor.h"
3
4static T *GetSTUPlayerComponent(AActor *PlayerPawn)
5{
6    // ...
7}

AActor提供成员函数FindComponentByClass


返回最近的Actor

ShootThemUp: Components/STUAIPerceptionComponent.h

public

1AActor *GetClosestEnemy() const;

获取所有捕捉的Actor

1TArray<AActor*> PercieveActors;
2GetCurrentlyPerceivedActors(UAISense_Sight::StaticClass(), PercieveActors);
3if (!PercieveActors.Num()) return nullptr;

计算最近Actor

 1const auto Controller = Cast<AAIController>(GetOwner());
 2if (!Controller) return nullptr;
 3
 4const auto Pawn = Controller->GetPawn();
 5if (!Pawn) return nullptr;
 6const auto LocalPos = Pawn->GetActorLocation();
 7
 8float BestDistance = MAX_FLT;
 9AActor *BestPawn = nullptr;
10for (const auto PercieveActor: PercieveActors)
11{
12    const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(PercieveActor);
13    if (HealthComponent && !HealthComponent->IsDead()) // TODO: check if enemies or not
14    {
15        const auto CurrentDistance = (PercieveActor->GetActorLocation() - LocalPos).Size();
16
17        if (CurrentDistance < BestDistance)
18        {
19            BestDistance = CurrentDistance;
20            BestPawn = PercieveActor;
21        }
22    }
23}

完整实现

ShootThemUp: Components/STUAIPerceptionComponent.cpp

 1#include "AIController.h"
 2#include "STUUtils.h"
 3#include "Components/STUHealthComponent.h"
 4#include "Perception/AISense_Sight.h"
 5
 6AActor *USTUAIPerceptionComponent::GetClosestEnemy() const
 7{
 8    TArray<AActor*> PercieveActors;
 9    GetCurrentlyPerceivedActors(UAISense_Sight::StaticClass(), PercieveActors);
10    if (!PercieveActors.Num()) return nullptr;
11
12    const auto Controller = Cast<AAIController>(GetOwner());
13    if (!Controller) return nullptr;
14
15    const auto Pawn = Controller->GetPawn();
16    if (!Pawn) return nullptr;
17    const auto LocalPos = Pawn->GetActorLocation();
18
19    float BestDistance = MAX_FLT;
20    AActor *BestPawn = nullptr;
21    for (const auto PercieveActor: PercieveActors)
22    {
23        const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(PercieveActor);
24        if (HealthComponent && !HealthComponent->IsDead()) // TODO: check if enemies or not                 
25        {
26            const auto CurrentDistance = (PercieveActor->GetActorLocation() - LocalPos).Size();
27            if (CurrentDistance < BestDistance)
28            {
29                BestDistance = CurrentDistance;
30                BestPawn = PercieveActor;
31            }
32        }
33    }
34    return BestPawn;
35}

添加Tick函数: 每秒计算一次最近Actor, 旋转NPC朝向被捕捉对象

暂时不检查STUAIPerceptionComponent是否有效

ShootThemUp: AI/STUAIController.h

protected

1virtual void Tick(float DeltaTime) override;

ShootThemUp: AI/STUAIController.cpp

1void ASTUAIController::Tick(float DeltaTime)
2{
3    Super::Tick(DeltaTime);
4    const auto AimActor = STUAIPerceptionComponent->GetClosestEnemy();
5    SetFocus(AimActor);
6}

SetFocus函数

设置控制器旋转, 使之前进向量朝向给定Actor


查看

打开AI调试信息, 使能Perception

在监控释放半径限定的圆内, 除非躲在障碍物之后, 不会脱离NPC捕捉


说明

AI Perception Component

允许NPC游戏角色感知给定范围内的世界中的其他Actor, 通过视觉和听觉感知敌人, 对伤害作出反应

本节为NPC添加视觉感知, 寻找与之最近的敌人, 使NPC面向该敌人


创建C++类

-
基类 AIPerceptionComponent
Components
Public
STUAIPerceptionComponent

为AIController添加感知组件

  • 添加构造函数

    ShootThemUp: AI/STUAIController.h

    public
    1ASTUAIController();
  • 添加组件

    ShootThemUp: AI/STUAIController.h

    protected
    1class USTUAIPerceptionComponent;
    2
    3UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
    4USTUAIPerceptionComponent *STUAIPerceptionComponent;
  • 初始化组件

    ShootThemUp: AI/STUAIController.cpp
    1#include "Components/STUAIPerceptionComponent.h"
    2
    3ASTUAIController::ASTUAIController()
    4{
    5    STUAIPerceptionComponent = CreateDefaultSubobject<USTUAIPerceptionComponent>("STUPerceptionComponent");
    6    SetPerceptionComponent(*STUAIPerceptionComponent);
    7}

设置感知组件

BP_STUAIController > STUAIPerceptionComponent > Details

AI Perception > Senses Config : 对游戏角色的感官进行配置, 可以感知的有伤害, 听觉, 视觉等


添加AI视觉


选择 AI Sight config


设置捕捉可视半径为1000 Sense > Sight Radius

设置监控释放半径为1500 Sense > Lose Sight Radius

设置可视半角为60 Sense > PeripheralVisionHalfAngleDegrees

设置调试颜色为蓝色 Sense > Debug Color

Sense > Detection by Affiliation , 勾选 Detect Enemies , Detect Neutrals , Detect Friendlies ; 当前没有分队别

设置MaxAge为1: Actor从视野中消失多久后不再保存相关信息



设置感官优先级

可以将 Sense > DominantSense 设置为None或 AISense_Sight



查看

  • 关闭行为树

    BP_STUCharactr

    按下 Option , 左键点击箭头


    UE4.27: 移除Root到Sequence的箭头即可

    UE5.1: 必须移除Sequence到子任务的箭头

  • 在新窗口运行游戏


  • 按下 ' 打开AI调试信息; 在小写键盘按下数字4, 使能 Perception 信息

  • 绿色圆柱体表示捕捉可视边界; 粉色圆柱体表示监控释放边界


  • 进入到捕捉可视区域, 游戏角色所处位置出现蓝色调试球体, 表明游戏角色被NPC捕捉到; 绿色可视角


  • 蓝色调试球体的活动范围由可视角和监控释放边界组成

    进入到捕捉可视区域, 被NPC捕捉, 之后只要在活动范围内, 其移动就会被NPC捕捉到

    活动范围内, 位于障碍物之后, MaxAge时间后, 蓝色调试球消失

    移动到活动范围外, MaxAge时间后, 蓝色调试球消失

  • 在场景中添加另一个NPC


场景中有多个游戏角色: NPC靠近和他距离最近的游戏角色


获取组件

GetComponentByClass也是AActor的成员函数: 修改传参类型

public

ShootThemUp: STUUtils.h

1// #include "GameFramework/Pawn.h"
2#include "GameFramework/Actor.h"
3
4static T *GetSTUPlayerComponent(AActor *PlayerPawn)
5{
6    // ...
7}

AActor提供成员函数FindComponentByClass


返回最近的Actor

ShootThemUp: Components/STUAIPerceptionComponent.h

public

1AActor *GetClosestEnemy() const;

获取所有捕捉的Actor

1TArray<AActor*> PercieveActors;
2GetCurrentlyPerceivedActors(UAISense_Sight::StaticClass(), PercieveActors);
3if (!PercieveActors.Num()) return nullptr;

计算最近Actor

 1const auto Controller = Cast<AAIController>(GetOwner());
 2if (!Controller) return nullptr;
 3
 4const auto Pawn = Controller->GetPawn();
 5if (!Pawn) return nullptr;
 6const auto LocalPos = Pawn->GetActorLocation();
 7
 8float BestDistance = MAX_FLT;
 9AActor *BestPawn = nullptr;
10for (const auto PercieveActor: PercieveActors)
11{
12    const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(PercieveActor);
13    if (HealthComponent && !HealthComponent->IsDead()) // TODO: check if enemies or not
14    {
15        const auto CurrentDistance = (PercieveActor->GetActorLocation() - LocalPos).Size();
16
17        if (CurrentDistance < BestDistance)
18        {
19            BestDistance = CurrentDistance;
20            BestPawn = PercieveActor;
21        }
22    }
23}

完整实现

ShootThemUp: Components/STUAIPerceptionComponent.cpp

 1#include "AIController.h"
 2#include "STUUtils.h"
 3#include "Components/STUHealthComponent.h"
 4#include "Perception/AISense_Sight.h"
 5
 6AActor *USTUAIPerceptionComponent::GetClosestEnemy() const
 7{
 8    TArray<AActor*> PercieveActors;
 9    GetCurrentlyPerceivedActors(UAISense_Sight::StaticClass(), PercieveActors);
10    if (!PercieveActors.Num()) return nullptr;
11
12    const auto Controller = Cast<AAIController>(GetOwner());
13    if (!Controller) return nullptr;
14
15    const auto Pawn = Controller->GetPawn();
16    if (!Pawn) return nullptr;
17    const auto LocalPos = Pawn->GetActorLocation();
18
19    float BestDistance = MAX_FLT;
20    AActor *BestPawn = nullptr;
21    for (const auto PercieveActor: PercieveActors)
22    {
23        const auto HealthComponent = STUUtils::GetSTUPlayerComponent<USTUHealthComponent>(PercieveActor);
24        if (HealthComponent && !HealthComponent->IsDead()) // TODO: check if enemies or not                 
25        {
26            const auto CurrentDistance = (PercieveActor->GetActorLocation() - LocalPos).Size();
27            if (CurrentDistance < BestDistance)
28            {
29                BestDistance = CurrentDistance;
30                BestPawn = PercieveActor;
31            }
32        }
33    }
34    return BestPawn;
35}

添加Tick函数: 每秒计算一次最近Actor, 旋转NPC朝向被捕捉对象

暂时不检查STUAIPerceptionComponent是否有效

ShootThemUp: AI/STUAIController.h

protected

1virtual void Tick(float DeltaTime) override;

ShootThemUp: AI/STUAIController.cpp

1void ASTUAIController::Tick(float DeltaTime)
2{
3    Super::Tick(DeltaTime);
4    const auto AimActor = STUAIPerceptionComponent->GetClosestEnemy();
5    SetFocus(AimActor);
6}

SetFocus函数

设置控制器旋转, 使之前进向量朝向给定Actor


查看

打开AI调试信息, 使能Perception

在监控释放半径限定的圆内, 除非躲在障碍物之后, 不会脱离NPC捕捉