为AI增加感官(视觉与听觉)需要用到UPawnSensingComponent,需要引入头文件#include "Perception/PawnSensingComponent.h"
在Enemy类中增加感官组件,并增加两个FSciptDelegate,用于后面代理视觉与听觉的方法。
1 FScriptDelegate SightDelegate;
2 FScriptDelegate HearDelegate;
3
4 UPROPERTY(VisibleAnywhere, Category = "AI | PawSensing")
5 class UPawnSensingComponent* Sense;
设置视觉范围,半径,是否视觉回调,是否只看见玩家角色,设置最小听觉阈值,设置最大范围听觉阈值,是否听觉回调,最大听觉年龄。
并设置好两个代理所绑定的方法,当AI听见与看见的时候就回执行这俩方法。
1 //UPawnSensingComponent
2 Sense = CreateDefaultSubobject<UPawnSensingComponent>(TEXT("Sense"));
3 Sense->bSeePawns = true;
4 Sense->SetPeripheralVisionAngle(60);
5 Sense->SightRadius = 500.0f;
6 Sense->bOnlySensePlayers = true;
7
8 Sense->HearingThreshold = 100.0f;
9 Sense->LOSHearingThreshold = 2000.0f;
10 Sense->bHearNoises = true;
11 Sense->HearingMaxSoundAge = 1.0;
12
13 Sense->SetSensingInterval(0.5f);
14 SightDelegate.BindUFunction(this, "SeeAndChangeBoard");
15 HearDelegate.BindUFunction(this, "HearAndChangeBoard");
16 }
1 void AEnemy::BeginPlay()
2 {
3 Super::BeginPlay();
4 UClass* Anima = LoadClass<UAnimInstance>(NULL,TEXT("AnimBlueprint'/Game/Mannequin/Animations/ThirdPerson_AnimBP.ThirdPerson_AnimBP_C'"));
5 skeletal->SetAnimInstanceClass(Anima);
6
7 //Binding Delegate
8 Sense->OnSeePawn.Add(SightDelegate);
9 Sense->OnHearNoise.Add(HearDelegate);
10 }
OnSeePawn是当AI看见玩家时候该执行什么代理。同理OnHearNoise。
执行看见玩家时候的代理方法如下:
1 void AEnemy::SeeAndChangeBoard(APawn* pawn)
2 {
3 AAI_MyProject16Character* PlayerPawn = Cast<AAI_MyProject16Character>(pawn);
4 AMyAIController* EnemyController = Cast<AMyAIController>(GetController());
5
6 if (Sense->CouldSeePawn(PlayerPawn)) {
7 if (Sense->HasLineOfSightTo(PlayerPawn)) {
8 if (EnemyController) {
9 EnemyController->myBoard->SetValueAsObject("Target", PlayerPawn);
10 EnemyController->myBoard->SetValueAsBool("isSeen", true);
11 //GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "Seen the pawn and UpdateBlackboard!");
12 }
13 }
14 }
15 }
该方法需要传入一个Pawn类对象指针,不能忘。
且在if语句中通过Sense调用CouldSeePawn和HasLineOfSightTo方法来判断他AI是否看到了玩家,是否有障碍阻挡。
运行此方法时候,会对黑板的值进行修改,这里在黑板增加一个bool类型数据,起名isSeen。
执行听见玩家时候的代理方法如下:
1 void AEnemy::HearAndChangeBoard(APawn* PawnInstigator, const FVector&Location, float Volume)
2 {
3 AAI_MyProject16Character* PlayerPawn = Cast<AAI_MyProject16Character>(PawnInstigator);
4 AMyAIController* EnemyController = Cast<AMyAIController>(GetController());
5
6 if (Sense->CanHear(Location, Volume, false)) {
7 if (EnemyController) {
8 EnemyController->myBoard->SetValueAsObject("Target", PlayerPawn);
9 EnemyController->myBoard->SetValueAsBool("isSeen", true);
10 //GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, "Hear the pawn and UpdateBlackboard!");
11 }
12 }
13
14 }
与看见相同。
参数需要三个,且在if语句中通过Sense调用CanHear方法来判断他AI是否听到了玩家。
最后再增加一个装饰器,起名isChasing。
1 // Fill out your copyright notice in the Description page of Project Settings.
2
3 #pragma once
4
5 #include "CoreMinimal.h"
6 #include "BehaviorTree/BTDecorator.h"
7 #include "AI_MyProject16Character.h"
8 #include "isChasing.generated.h"
9
10 /**
11 *
12 */
13 UCLASS()
14 class AI_MYPROJECT16_API UisChasing : public UBTDecorator
15 {
16 GENERATED_BODY()
17
18 public:
19 UisChasing();
20
21 virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const;
22
23 UPROPERTY(VisibleAnywhere,BlueprintReadOnly)
24 float dist;
25 };
希望玩家与AI之间距离大与dist的时候,原地停止5秒钟。CalculateRawConditionValue是继承自父类的方法。
1 // Fill out your copyright notice in the Description page of Project Settings.
2
3
4 #include "isChasing.h"
5 #include "MyAIController.h"
6 #include "Enemy.h"
7 #include "Engine.h"
8
9 UisChasing::UisChasing() {
10 bCreateNodeInstance = true;
11 dist = 100;
12 }
13
14 bool UisChasing::CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const{
15
16
17 AAI_MyProject16Character* PlayerPawn = Cast<AAI_MyProject16Character>(GetWorld()->GetFirstPlayerController()->GetPawn());
18 FVector targetLocation = PlayerPawn->GetActorLocation();
19
20 AMyAIController* enemyController = Cast<AMyAIController>(OwnerComp.GetAIOwner());
21
22
23 AEnemy* enemyPawn = Cast<AEnemy>(enemyController->GetPawn());
24
25
26 FVector SelfLocation = enemyPawn->GetActorLocation();
27
28 if (PlayerPawn && enemyPawn) {
29 float dists = (targetLocation - SelfLocation).Size();
30
31 if (dists <= dist) {
32 GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Yellow, "Check!");
33 return false;
34 }
35 else
36 {
37 GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Yellow, "UnCheck!");
38 return true;
39 }
40 }
41 else {
42 return false;
43 }
44 }
装饰器只表示逻辑是否满足。
所以有返回值。
编译运行即可,本节不展示结果。