上文中我们重写了Possess函数,在possess函数中,运行我们的行为树,既然有Possess肯定就有UnPossess啦,因此,我们需要重写UnPossess函数,在该函数中,停止运行我们的行为树,如下所示:
void AAIBlogController::UnPossess()
{
Super::UnPossess();
if (BehaviorTreeCompo != nullptr)
{
BehaviorTreeCompo->StopTree(EBTStopMode::Safe); // 停止运行行为树
}
}
接下来就要修改我们的AICharacter了,还记得我们为AICharacter添加了一个PawnSenceComponent吗?该组件是用来寻找敌人的,使我们的AI有视力,我们需要为该组件绑定函数,当看到敌人时,应该怎么做,显然。在我们这里,在看到敌人时,就更新我们的黑板数据,使我们的AI能知道移动到哪里去。通过查阅ue4的api,知道我们有一个OnSeePawn的代理,因此需要为该代理绑定函数。如下所示:
UFUNCTION()
void HaveSeePawn(APawn* Pawn);
在BeginPlay中,绑定HaveSeePawn,如下所示:
void AAIBlogCharacter::BeginPlay()
{
Super::BeginPlay();
if (Sight != nullptr)
{
Sight->OnSeePawn.AddDynamic(this, &AAIBlogCharacter::HaveSeePawn);
}
}
显然,在我们的HaveSeePawn函数中,应该根据我们看到的pawn,获得它的位置,从而根据其位置,来设置我们的黑板数据的值,查阅api,发现只有UBlackBoradComponent才有set值的函数,因此,修改我们的成员,如下所示:
public:
//class UBlackboardData* HeroData;
class UBlackboardComponent* HeroData;
修改我们的possess函数,如下所示:
void AAIBlogController::Possess(APawn* InPawn)
{
AAIBlogCharacter* AIC = Cast<AAIBlogCharacter>(InPawn);
if (AIC == nullptr)
{
return;
}
Super::Possess(InPawn);
if (BehaviorTreeCompo != nullptr)
{
if (BehaviorTree != nullptr)
{
BehaviorTreeCompo->StartTree(*BehaviorTree); // 运行行为树
//AIC->HeroData = BehaviorTree->BlackboardAsset; // 设置黑板资源,方便以后更新黑板资源成员数据
if (AIC->HeroData != nullptr)
{
AIC->HeroData->InitializeBlackboard(*BehaviorTree->BlackboardAsset); // 初始化
}
}
}
}
接下来在HaveSeePawn中,就可以更新我们的值啦,需要注意的是,字段名字一定要与黑板资源上的一致,如下所示:
void AAIBlogCharacter::HaveSeePawn(APawn* Pawn)
{
ABlogProjectCharacter* Player = Cast<ABlogProjectCharacter>(Pawn);
if (Player != nullptr)
{
FVector Location = Player->GetActorLocation(); // 获取目标的位置,这是世界坐标
if (HeroData != nullptr)
{
HeroData->SetValueAsVector("TargetLocation", Location); // 注意,这里的Location字段要和黑板资源里的一致
}
}
}
行为树的节点那些就留到下文再讲吧。该文就到这里啦。