一、自定义任务节点
- 本节需要创建自定义任务节点
BTTask
,实现小兵进入角色范围后进射击的功能。 - 对于
BTTaskNode
的子类,我们需要在代码中重写ExecuteTask
函数即可。返回值为EBTNodeResult::Failed
和EBTNodeResult::Successd
,对应行为树的节点返回失败或者成功。
//SBTTask_RangeAttack.h
UCLASS()
class FPSPROJECT_API USBTTask_RangeAttack : public UBTTaskNode
{
GENERATED_BODY()
protected:
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
//用于角色发射投掷物攻击
UPROPERTY(EditAnywhere, Category = "AI")
TSubclassOf<AActor> ProjectileClass;
};
//SBTTask_RangeAttack.cpp
EBTNodeResult::Type USBTTask_RangeAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
AAIController* MyController = OwnerComp.GetAIOwner();
if(ensure(MyController))
{
ACharacter* MyPawn = Cast<ACharacter>(MyController->GetPawn());
if(MyPawn == nullptr)
{
return EBTNodeResult::Failed;
}
AActor* TargetActor = Cast<AActor>(OwnerComp.GetBlackboardComponent()->GetValueAsObject("TargetActor"));
if(TargetActor == nullptr)
{
return EBTNodeResult::Failed;
}
FVector MuzzleLocation = MyPawn->GetMesh()->GetSocketLocation("Muzzle_01");
//方向向量=目标位置-当前位置
FVector Direction = TargetActor->GetActorLocation() - MuzzleLocation;
FRotator MuzzleRotation = Direction.Rotation();
FActorSpawnParameters params;
params.Instigator = MyPawn;
params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
ensure(ProjectileClass);
AActor* NewProj = GetWorld()->SpawnActor<AActor>(ProjectileClass, MuzzleLocation, MuzzleRotation);
return NewProj ? EBTNodeResult::Succeeded : EBTNodeResult::Failed;
}
return EBTNodeResult::Failed;
}
二、优化
1. 子弹发射冷却
Cooldown
节点会控制节点执行的间隔,当节点处于冷却时间时,会直接返回false
,以下节点自然都不会执行。Loop节点则会循环执行以下节点。
此时AI的逻辑:如果目标在攻击范围外,会向目标移动。进入攻击范围后,开枪向角色以0.5s为间隔时间射击三次,冷却2s。在此期间,行为树会运行后面两个节点,再重新判断是否结束冷却。
2. 攻击时面朝玩家
行为树自带了一个修改AI小兵的朝向服务BTService
:Set defalt focus
,从而不需要再使用SetActorRotation
函数
- 最终的完整图(包括之后的EQS):