Smart Enemy AI
January 22, 2026About 2 min
Smart Enemy AI
Behavior Tree
Basic Enemy Behavior

AIMoveTo节点在C++中的实现:
UAIAsyncTaskBlueprintProxy* UAIBlueprintHelperLibrary::CreateMoveToProxyObject(...)
在场景中添加导航网格,然后按P显示:

上面的逻辑替换为BehaviorTree:
黑板设置:

在AIController中初始化行为树,设置黑板数值。
void ABaseAIController::InitDefaultBT()
{
RunBehaviorTree(DefaultBT);
ACharacter* TargetPlayer = UGameplayStatics::GetPlayerCharacter(this, 0);
// 使用字符串对黑板中的对象进行绑定
Blackboard->SetValueAsObject("AttackTarget", TargetPlayer);
}
Custom Task Node
EBTNodeResult::Type UBTTask_DefaultAttack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
AAIController* const MyController = OwnerComp.GetAIOwner();
EBTNodeResult::Type Result = EBTNodeResult::Failed;
if (MyController->GetPawn()->Implements<UEnemyInterface>())
{
IEnemyInterface::Execute_Attack(MyController->GetPawn());
Result = EBTNodeResult::Succeeded;
}
return Result;
}

添加 自定义节点自己控制结束

常实现方法:
- 激活:virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
- 退出:virtual EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
- 轮询:virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
Blackboard Selector
头文件:
UCLASS()
class SMARTENEMYAI_API UBTTask_FocusTarget : public UBTTaskNode
{
GENERATED_BODY()
public:
UBTTask_FocusTarget(const FObjectInitializer& ObjectInitialize);
protected:
virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
virtual void InitializeFromAsset(UBehaviorTree& Asset) override;
protected:
/** blackboard key selector */
UPROPERTY(EditAnywhere, Category = Blackboard)
struct FBlackboardKeySelector AttackTargetKey;
};
实现:
#include "AI/Task/BTTask_FocusTarget.h"
#include "AIController.h"
#include "BehaviorTree/BlackboardComponent.h"
UBTTask_FocusTarget::UBTTask_FocusTarget(const FObjectInitializer& ObjectInitialize)
{
NodeName = "Focus Target";
// 设置过滤
AttackTargetKey.AddObjectFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FocusTarget, AttackTargetKey), AActor::StaticClass());
}
EBTNodeResult::Type UBTTask_FocusTarget::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
EBTNodeResult::Type Result = EBTNodeResult::Failed;
AAIController* const MyController = OwnerComp.GetAIOwner();
UBlackboardComponent* Blackboard = OwnerComp.GetBlackboardComponent();
if (!MyController || !Blackboard || !AttackTargetKey.IsSet())
{
return Result;
}
if (AActor* Target = Cast<AActor>(Blackboard->GetValueAsObject(AttackTargetKey.SelectedKeyName)))
{
MyController->SetFocus(Target);
Result = EBTNodeResult::Succeeded;
}
return Result;
}
void UBTTask_FocusTarget::InitializeFromAsset(UBehaviorTree& Asset)
{
Super::InitializeFromAsset(Asset);
// 从资产中解析选中的键名
if (const UBlackboardData* BBAsset = GetBlackboardAsset())
{
AttackTargetKey.ResolveSelectedKey(*BBAsset);
}
else
{
AttackTargetKey.InvalidateResolvedKey();
}
}
Set Focus
在使用AAIController::SetFocus后,想要角色跟着旋转需要在角色上开启以下选项:

Custom Decorator
提前判断任务是否可以执行的节点

主要实现方法:
- virtual bool CalculateRawConditionValue(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override;
最终的行为树:
