$npx -y skills add quodsoler/unreal-engine-skills --skill ue-ai-navigationUse this skill when implementing AI, AIController, behavior tree, blackboard, AI perception, NavMesh, EQS, navigation, pathfinding, State Tree, or Smart Objects in Unreal Engine. See references/behavior-tree-patterns.md for BT patterns and references/eqs-reference.md for EQS conf
| 1 | # UE AI and Navigation |
| 2 | |
| 3 | You are an expert in Unreal Engine's AI and navigation systems. |
| 4 | |
| 5 | ## Context |
| 6 | |
| 7 | Read `.agents/ue-project-context.md` for project AI plugins, subsystem configs, enabled modules (AIModule, NavigationSystem, GameplayStateTreeModule, SmartObjectsModule), and existing AI frameworks. |
| 8 | |
| 9 | ## Information Gathering |
| 10 | |
| 11 | Before implementing, clarify: AI complexity, navigation needs (ground/fly/swim, dynamic obstacles, streaming), perception senses required, Behavior Tree vs. State Tree preference, multiplayer authority model, and agent count for budget planning. |
| 12 | |
| 13 | --- |
| 14 | |
| 15 | ## AI Architecture |
| 16 | |
| 17 | ``` |
| 18 | APawn |
| 19 | └── AAIController (server-only in multiplayer) |
| 20 | ├── UBehaviorTreeComponent (UBrainComponent subclass) |
| 21 | │ └── UBehaviorTree asset → UBlackboardData |
| 22 | ├── UBlackboardComponent (AI knowledge store) |
| 23 | ├── UAIPerceptionComponent (sight, hearing, damage) |
| 24 | └── UPathFollowingComponent (NavMesh path execution) |
| 25 | ``` |
| 26 | |
| 27 | **Build.cs modules**: `AIModule`, `NavigationSystem`, `GameplayTasks` |
| 28 | |
| 29 | --- |
| 30 | |
| 31 | ## AIController |
| 32 | |
| 33 | ```cpp |
| 34 | // MyAIController.h |
| 35 | UCLASS() |
| 36 | class AMyAIController : public AAIController |
| 37 | { |
| 38 | GENERATED_BODY() |
| 39 | public: |
| 40 | AMyAIController(); |
| 41 | UPROPERTY(EditDefaultsOnly, Category = AI) |
| 42 | TObjectPtr<UBehaviorTree> BehaviorTreeAsset; |
| 43 | protected: |
| 44 | virtual void OnPossess(APawn* InPawn) override; |
| 45 | UFUNCTION() |
| 46 | void OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus); |
| 47 | }; |
| 48 | |
| 49 | // MyAIController.cpp |
| 50 | AMyAIController::AMyAIController() |
| 51 | { |
| 52 | bStartAILogicOnPossess = true; |
| 53 | bStopAILogicOnUnposses = true; |
| 54 | // PerceptionComponent declared in AAIController; configure senses here or in BP defaults |
| 55 | } |
| 56 | |
| 57 | void AMyAIController::OnPossess(APawn* InPawn) |
| 58 | { |
| 59 | Super::OnPossess(InPawn); |
| 60 | if (BehaviorTreeAsset) |
| 61 | RunBehaviorTree(BehaviorTreeAsset); // calls UseBlackboard internally |
| 62 | if (UAIPerceptionComponent* PC = GetAIPerceptionComponent()) |
| 63 | PC->OnTargetPerceptionUpdated.AddDynamic(this, &AMyAIController::OnTargetPerceptionUpdated); |
| 64 | } |
| 65 | ``` |
| 66 | |
| 67 | ### Key AAIController API |
| 68 | |
| 69 | ```cpp |
| 70 | // Navigation |
| 71 | EPathFollowingRequestResult::Type MoveToActor(AActor* Goal, float AcceptanceRadius = -1, |
| 72 | bool bStopOnOverlap = true, bool bUsePathfinding = true, bool bCanStrafe = true, |
| 73 | TSubclassOf<UNavigationQueryFilter> FilterClass = {}, bool bAllowPartialPath = true); |
| 74 | |
| 75 | EPathFollowingRequestResult::Type MoveToLocation(const FVector& Dest, float AcceptanceRadius = -1, |
| 76 | bool bStopOnOverlap = true, bool bUsePathfinding = true, |
| 77 | bool bProjectDestinationToNavigation = false, bool bCanStrafe = true, |
| 78 | TSubclassOf<UNavigationQueryFilter> FilterClass = {}, bool bAllowPartialPath = true); |
| 79 | |
| 80 | void StopMovement(); |
| 81 | bool HasPartialPath() const; |
| 82 | EPathFollowingStatus::Type GetMoveStatus() const; |
| 83 | |
| 84 | // Focus |
| 85 | void SetFocus(AActor* NewFocus, EAIFocusPriority::Type Priority = EAIFocusPriority::Gameplay); |
| 86 | void SetFocalPoint(FVector NewFocus, EAIFocusPriority::Type Priority = EAIFocusPriority::Gameplay); |
| 87 | void ClearFocus(EAIFocusPriority::Type Priority); |
| 88 | |
| 89 | // Brain / Blackboard |
| 90 | bool RunBehaviorTree(UBehaviorTree* BTAsset); |
| 91 | bool UseBlackboard(UBlackboardData* BlackboardAsset, UBlackboardComponent*& BlackboardComponent); |
| 92 | UBlackboardComponent* GetBlackboardComponent(); |
| 93 | |
| 94 | // Team (IGenericTeamAgentInterface) |
| 95 | void SetGenericTeamId(const FGenericTeamId& NewTeamID); |
| 96 | |
| 97 | // Delegate: FAIMoveCompletedSignature ReceiveMoveCompleted (RequestID, Result) |
| 98 | ``` |
| 99 | |
| 100 | **On Pawn**: `AIControllerClass = AMyAIController::StaticClass(); AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;` |
| 101 | |
| 102 | --- |
| 103 | |
| 104 | ## Blackboard |
| 105 | |
| 106 | | Type | Get | Set | |
| 107 | |------|-----|-----| |
| 108 | | Object | `GetValueAsObject` | `SetValueAsObject` | |
| 109 | | Vector | `GetValueAsVector` | `SetValueAsVector` | |
| 110 | | Bool | `GetValueAsBool` | `SetValueAsBool` | |
| 111 | | Float | `GetValueAsFloat` | `SetValueAsFloat` | |
| 112 | | Int | `GetValueAsInt` | `SetValueAsInt` | |
| 113 | | Enum | `GetValueAsEnum` | `SetValueAsEnum` | |
| 114 | | Name | `GetValueAsName` | `SetValueAsName` | |
| 115 | | Rotator | `GetValueAsRotator` | `SetValueAsRotator` | |
| 116 | | String | `GetValueAsString` | `SetValueAsString` | |
| 117 | | Class | `GetValueAsClass` | `SetValueAsClass` | |
| 118 | |
| 119 | ```cpp |
| 120 | UBlackboardComponent* BB = GetBlackboardComponent(); |
| 121 | BB->SetValueAsObject(TEXT("TargetActor"), SomeActor); |
| 122 | BB->SetValueAsVector(TEXT("LastKnownLocation"), Location); |
| 123 | BB->ClearValue(TEXT("TargetActor")); |
| 124 | bool bSet = BB->IsVectorValueSet(TEXT("PatrolLocation")); |
| 125 | |
| 126 | // Observer (called when key changes) |
| 127 | FBlackboard::FKey KeyID = BB->GetKeyID(TEXT("TargetActor")); |
| 128 | FDelegateHandle H = BB->RegisterObserver(KeyID, this, |
| 129 | FOnBlackboardChangeNotification::CreateUObject(this, &AMyAICont |