claude-code-arcane 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +131 -41
  3. package/agents/game/ue-blueprint-specialist.md +125 -0
  4. package/agents/game/ue-gas-specialist.md +133 -0
  5. package/agents/game/ue-umg-specialist.md +131 -0
  6. package/dist/cli.js +642 -478
  7. package/docs/SKILLS-CATALOG.md +42 -3
  8. package/docs/USER-GUIDE.md +28 -54
  9. package/hooks/detect-unreal.sh +90 -0
  10. package/package.json +2 -1
  11. package/profiles/agile.yaml +1 -1
  12. package/profiles/ai.yaml +1 -1
  13. package/profiles/android-native.yaml +1 -1
  14. package/profiles/audio.yaml +1 -1
  15. package/profiles/backend-dotnet.yaml +1 -1
  16. package/profiles/backend-go.yaml +1 -1
  17. package/profiles/backend-nestjs.yaml +1 -1
  18. package/profiles/backend-nextjs.yaml +1 -1
  19. package/profiles/backend-ts.yaml +1 -1
  20. package/profiles/business.yaml +1 -1
  21. package/profiles/clevel.yaml +1 -1
  22. package/profiles/clickup.yaml +1 -1
  23. package/profiles/core.yaml +1 -1
  24. package/profiles/database.yaml +1 -1
  25. package/profiles/design.yaml +1 -1
  26. package/profiles/docs.yaml +1 -1
  27. package/profiles/ecommerce.yaml +1 -1
  28. package/profiles/finance.yaml +1 -1
  29. package/profiles/flutter.yaml +1 -1
  30. package/profiles/freelance.yaml +1 -1
  31. package/profiles/frontend.yaml +1 -1
  32. package/profiles/infra.yaml +1 -1
  33. package/profiles/integrations.yaml +1 -1
  34. package/profiles/ios-native.yaml +1 -1
  35. package/profiles/jira.yaml +1 -1
  36. package/profiles/job-hunt.yaml +1 -1
  37. package/profiles/marketing.yaml +1 -1
  38. package/profiles/mobile.yaml +1 -1
  39. package/profiles/regulatory.yaml +1 -1
  40. package/profiles/second-brain.yaml +1 -1
  41. package/profiles/security.yaml +1 -1
  42. package/profiles/self-improving.yaml +1 -1
  43. package/profiles/statusline.yaml +1 -1
  44. package/profiles/testing.yaml +1 -1
  45. package/profiles/unity-design.yaml +1 -1
  46. package/profiles/unity-dev.yaml +1 -1
  47. package/profiles/unreal-dev.yaml +97 -0
  48. package/profiles/visual-novel.yaml +1 -1
  49. package/rules/gamedev/unreal-code.md +125 -0
  50. package/skills/arcane-list/SKILL.md +12 -7
  51. package/skills/install-mcp/SKILL.md +135 -46
  52. package/skills/install-mcp/references/unreal-operations.md +55 -0
  53. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  54. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  55. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  56. package/skills/ue-ai-navigation/SKILL.md +526 -0
  57. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  58. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  59. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  60. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  61. package/skills/ue-animation-system/SKILL.md +521 -0
  62. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  63. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  64. package/skills/ue-async-threading/SKILL.md +433 -0
  65. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  66. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  67. package/skills/ue-audio-system/SKILL.md +527 -0
  68. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  69. package/skills/ue-blueprints/SKILL.md +134 -0
  70. package/skills/ue-blueprints/references/communication.md +82 -0
  71. package/skills/ue-character-movement/SKILL.md +475 -0
  72. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  73. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  74. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  75. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  76. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  77. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  78. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  79. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  80. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  81. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  82. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  83. package/skills/ue-editor-tools/SKILL.md +523 -0
  84. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  85. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  86. package/skills/ue-game-features/SKILL.md +477 -0
  87. package/skills/ue-game-features/references/experience-system.md +222 -0
  88. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  89. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  90. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  91. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  92. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  93. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  94. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  95. package/skills/ue-input-system/SKILL.md +394 -0
  96. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  97. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  98. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  99. package/skills/ue-mass-entity/SKILL.md +476 -0
  100. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  101. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  102. package/skills/ue-materials-rendering/SKILL.md +526 -0
  103. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  104. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  105. package/skills/ue-module-build-system/SKILL.md +487 -0
  106. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  107. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  108. package/skills/ue-naming-conventions/SKILL.md +142 -0
  109. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  110. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  111. package/skills/ue-networking-replication/SKILL.md +452 -0
  112. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  113. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  114. package/skills/ue-niagara-effects/SKILL.md +531 -0
  115. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  116. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  117. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  118. package/skills/ue-packaging/SKILL.md +116 -0
  119. package/skills/ue-physics-collision/SKILL.md +423 -0
  120. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  121. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  122. package/skills/ue-procedural-generation/SKILL.md +505 -0
  123. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  124. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  125. package/skills/ue-project-context/SKILL.md +305 -0
  126. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  127. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  128. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  129. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  130. package/skills/ue-state-trees/SKILL.md +489 -0
  131. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  132. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  133. package/skills/ue-testing-debugging/SKILL.md +520 -0
  134. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  135. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  136. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  137. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  138. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  139. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  140. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  141. package/templates/gamedev/unreal-project-context.md +107 -0
@@ -0,0 +1,526 @@
1
+ ---
2
+ name: ue-ai-navigation
3
+ description: "AI controllers, behavior trees, blackboards, AI perception, NavMesh, EQS"
4
+ category: "gamedev"
5
+ argument-hint: "[behavior o AI feature]"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit
8
+ ---
9
+
10
+ # UE AI and Navigation
11
+
12
+ You are an expert in Unreal Engine's AI and navigation systems.
13
+
14
+ Ask before writing: this skill edits AI controllers, behavior trees, blackboard and EQS assets. Propose the change and get approval before using Write or Edit.
15
+
16
+ ## Context
17
+
18
+ Read `docs/unreal/project-context.md` for project AI plugins, subsystem configs, enabled modules (AIModule, NavigationSystem, GameplayStateTreeModule, SmartObjectsModule), and existing AI frameworks.
19
+
20
+ ## Information Gathering
21
+
22
+ 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.
23
+
24
+ ---
25
+
26
+ ## AI Architecture
27
+
28
+ ```
29
+ APawn
30
+ └── AAIController (server-only in multiplayer)
31
+ ├── UBehaviorTreeComponent (UBrainComponent subclass)
32
+ │ └── UBehaviorTree asset → UBlackboardData
33
+ ├── UBlackboardComponent (AI knowledge store)
34
+ ├── UAIPerceptionComponent (sight, hearing, damage)
35
+ └── UPathFollowingComponent (NavMesh path execution)
36
+ ```
37
+
38
+ **Build.cs modules**: `AIModule`, `NavigationSystem`, `GameplayTasks`
39
+
40
+ ---
41
+
42
+ ## AIController
43
+
44
+ ```cpp
45
+ // MyAIController.h
46
+ UCLASS()
47
+ class AMyAIController : public AAIController
48
+ {
49
+ GENERATED_BODY()
50
+ public:
51
+ AMyAIController();
52
+ UPROPERTY(EditDefaultsOnly, Category = AI)
53
+ TObjectPtr<UBehaviorTree> BehaviorTreeAsset;
54
+ protected:
55
+ virtual void OnPossess(APawn* InPawn) override;
56
+ UFUNCTION()
57
+ void OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus);
58
+ };
59
+
60
+ // MyAIController.cpp
61
+ AMyAIController::AMyAIController()
62
+ {
63
+ bStartAILogicOnPossess = true;
64
+ bStopAILogicOnUnposses = true;
65
+ // PerceptionComponent declared in AAIController; configure senses here or in BP defaults
66
+ }
67
+
68
+ void AMyAIController::OnPossess(APawn* InPawn)
69
+ {
70
+ Super::OnPossess(InPawn);
71
+ if (BehaviorTreeAsset)
72
+ RunBehaviorTree(BehaviorTreeAsset); // calls UseBlackboard internally
73
+ if (UAIPerceptionComponent* PC = GetAIPerceptionComponent())
74
+ PC->OnTargetPerceptionUpdated.AddDynamic(this, &AMyAIController::OnTargetPerceptionUpdated);
75
+ }
76
+ ```
77
+
78
+ ### Key AAIController API
79
+
80
+ ```cpp
81
+ // Navigation
82
+ EPathFollowingRequestResult::Type MoveToActor(AActor* Goal, float AcceptanceRadius = -1,
83
+ bool bStopOnOverlap = true, bool bUsePathfinding = true, bool bCanStrafe = true,
84
+ TSubclassOf<UNavigationQueryFilter> FilterClass = {}, bool bAllowPartialPath = true);
85
+
86
+ EPathFollowingRequestResult::Type MoveToLocation(const FVector& Dest, float AcceptanceRadius = -1,
87
+ bool bStopOnOverlap = true, bool bUsePathfinding = true,
88
+ bool bProjectDestinationToNavigation = false, bool bCanStrafe = true,
89
+ TSubclassOf<UNavigationQueryFilter> FilterClass = {}, bool bAllowPartialPath = true);
90
+
91
+ void StopMovement();
92
+ bool HasPartialPath() const;
93
+ EPathFollowingStatus::Type GetMoveStatus() const;
94
+
95
+ // Focus
96
+ void SetFocus(AActor* NewFocus, EAIFocusPriority::Type Priority = EAIFocusPriority::Gameplay);
97
+ void SetFocalPoint(FVector NewFocus, EAIFocusPriority::Type Priority = EAIFocusPriority::Gameplay);
98
+ void ClearFocus(EAIFocusPriority::Type Priority);
99
+
100
+ // Brain / Blackboard
101
+ bool RunBehaviorTree(UBehaviorTree* BTAsset);
102
+ bool UseBlackboard(UBlackboardData* BlackboardAsset, UBlackboardComponent*& BlackboardComponent);
103
+ UBlackboardComponent* GetBlackboardComponent();
104
+
105
+ // Team (IGenericTeamAgentInterface)
106
+ void SetGenericTeamId(const FGenericTeamId& NewTeamID);
107
+
108
+ // Delegate: FAIMoveCompletedSignature ReceiveMoveCompleted (RequestID, Result)
109
+ ```
110
+
111
+ **On Pawn**: `AIControllerClass = AMyAIController::StaticClass(); AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;`
112
+
113
+ ---
114
+
115
+ ## Blackboard
116
+
117
+ | Type | Get | Set |
118
+ |------|-----|-----|
119
+ | Object | `GetValueAsObject` | `SetValueAsObject` |
120
+ | Vector | `GetValueAsVector` | `SetValueAsVector` |
121
+ | Bool | `GetValueAsBool` | `SetValueAsBool` |
122
+ | Float | `GetValueAsFloat` | `SetValueAsFloat` |
123
+ | Int | `GetValueAsInt` | `SetValueAsInt` |
124
+ | Enum | `GetValueAsEnum` | `SetValueAsEnum` |
125
+ | Name | `GetValueAsName` | `SetValueAsName` |
126
+ | Rotator | `GetValueAsRotator` | `SetValueAsRotator` |
127
+ | String | `GetValueAsString` | `SetValueAsString` |
128
+ | Class | `GetValueAsClass` | `SetValueAsClass` |
129
+
130
+ ```cpp
131
+ UBlackboardComponent* BB = GetBlackboardComponent();
132
+ BB->SetValueAsObject(TEXT("TargetActor"), SomeActor);
133
+ BB->SetValueAsVector(TEXT("LastKnownLocation"), Location);
134
+ BB->ClearValue(TEXT("TargetActor"));
135
+ bool bSet = BB->IsVectorValueSet(TEXT("PatrolLocation"));
136
+
137
+ // Observer (called when key changes)
138
+ FBlackboard::FKey KeyID = BB->GetKeyID(TEXT("TargetActor"));
139
+ FDelegateHandle H = BB->RegisterObserver(KeyID, this,
140
+ FOnBlackboardChangeNotification::CreateUObject(this, &AMyAIController::OnBBKeyChanged));
141
+ BB->UnregisterObserver(KeyID, H);
142
+
143
+ // High-perf cached accessor (avoids repeated name lookups):
144
+ FBBKeyCachedAccessor<UBlackboardKeyType_Bool> BBInCombat;
145
+ // Init: BBInCombat = FBBKeyCachedAccessor<...>(*BBComp, KeyID);
146
+ // Use: bool b = BBInCombat.Get(); BBInCombat.SetValue(*BB, true);
147
+ ```
148
+
149
+ Mark keys **Instance Synced** to share values across all AI using the same `UBlackboardData` (squad-wide alerts via `UAISystem` propagation).
150
+
151
+ ---
152
+
153
+ ## Behavior Tree Nodes
154
+
155
+ ### Custom Task
156
+
157
+ ```cpp
158
+ UCLASS()
159
+ class UMyBTTask_Attack : public UBTTaskNode
160
+ {
161
+ GENERATED_BODY()
162
+ public:
163
+ UMyBTTask_Attack() { NodeName = TEXT("Attack"); INIT_TASK_NODE_NOTIFY_FLAGS(); }
164
+ UPROPERTY(EditAnywhere) FBlackboardKeySelector TargetKey;
165
+ protected:
166
+ virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
167
+ virtual void TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override;
168
+ virtual EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
169
+ };
170
+
171
+ EBTNodeResult::Type UMyBTTask_Attack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
172
+ {
173
+ AActor* Target = Cast<AActor>(
174
+ OwnerComp.GetBlackboardComponent()->GetValueAsObject(TargetKey.SelectedKeyName));
175
+ if (!IsValid(Target)) return EBTNodeResult::Failed;
176
+ // Start async work → return InProgress; call FinishLatentTask() when done
177
+ return EBTNodeResult::InProgress;
178
+ }
179
+
180
+ void UMyBTTask_Attack::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
181
+ {
182
+ FinishLatentTask(OwnerComp, EBTNodeResult::Succeeded); // or Failed
183
+ }
184
+
185
+ EBTNodeResult::Type UMyBTTask_Attack::AbortTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
186
+ {
187
+ return EBTNodeResult::Aborted; // cleanup; or InProgress + FinishLatentAbort()
188
+ }
189
+ ```
190
+
191
+ ### Custom Decorator
192
+
193
+ ```cpp
194
+ UCLASS()
195
+ class UMyBTDecorator_CanSee : public UBTDecorator
196
+ {
197
+ GENERATED_BODY()
198
+ public:
199
+ UMyBTDecorator_CanSee()
200
+ {
201
+ INIT_DECORATOR_NODE_NOTIFY_FLAGS();
202
+ bAllowAbortLowerPri = true; bAllowAbortChildNodes = true;
203
+ FlowAbortMode = EBTFlowAbortMode::Both;
204
+ }
205
+ UPROPERTY(EditAnywhere) FBlackboardKeySelector TargetKey;
206
+ protected:
207
+ virtual bool CalculateRawConditionValue(
208
+ UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) const override
209
+ {
210
+ AActor* Target = Cast<AActor>(
211
+ OwnerComp.GetBlackboardComponent()->GetValueAsObject(TargetKey.SelectedKeyName));
212
+ return IsValid(Target) && OwnerComp.GetAIOwner()->LineOfSightTo(Target);
213
+ }
214
+ };
215
+ ```
216
+
217
+ ### Custom Service
218
+
219
+ ```cpp
220
+ UCLASS()
221
+ class UMyBTService_UpdateTarget : public UBTService
222
+ {
223
+ GENERATED_BODY()
224
+ public:
225
+ UMyBTService_UpdateTarget() { Interval = 0.5f; RandomDeviation = 0.1f; INIT_SERVICE_NODE_NOTIFY_FLAGS(); }
226
+ protected:
227
+ virtual void TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds) override
228
+ {
229
+ TArray<AActor*> Hostiles;
230
+ OwnerComp.GetAIOwner()->GetAIPerceptionComponent()->GetPerceivedHostileActors(Hostiles);
231
+ AActor* Best = nullptr; float BestDist = FLT_MAX;
232
+ FVector MyLoc = OwnerComp.GetAIOwner()->GetPawn()->GetActorLocation();
233
+ for (AActor* A : Hostiles)
234
+ {
235
+ float D = FVector::Dist(MyLoc, A->GetActorLocation());
236
+ if (D < BestDist) { BestDist = D; Best = A; }
237
+ }
238
+ OwnerComp.GetBlackboardComponent()->SetValueAsObject(TEXT("TargetActor"), Best);
239
+ }
240
+ };
241
+ ```
242
+
243
+ ### Built-in Nodes (reference)
244
+
245
+ **Tasks**: `BTTask_MoveTo`, `BTTask_MoveDirectlyToward`, `BTTask_Wait`, `BTTask_WaitBlackboardTime`, `BTTask_RunEQSQuery`, `BTTask_PlayAnimation`, `BTTask_MakeNoise`, `BTTask_RotateToFaceBBEntry`, `BTTask_RunBehavior`, `BTTask_RunBehaviorDynamic`, `BTTask_FinishWithResult`
246
+
247
+ **Decorators**: `BTDecorator_Blackboard`, `BTDecorator_CompareBBEntries`, `BTDecorator_Cooldown`, `BTDecorator_TagCooldown`, `BTDecorator_Loop`, `BTDecorator_TimeLimit`, `BTDecorator_DoesPathExist`, `BTDecorator_IsAtLocation`, `BTDecorator_CheckGameplayTagsOnActor`, `BTDecorator_ForceSuccess`
248
+
249
+ **Composites**: `Selector` (first success), `Sequence` (first failure), `SimpleParallel` (main task + background subtree; main task drives completion). UE does not ship a general-purpose Parallel node — `SimpleParallel` is the built-in option. For true parallel execution of N branches, implement a custom `UBTCompositeNode` or chain multiple `SimpleParallel` nodes.
250
+
251
+ ---
252
+
253
+ ## AI Perception
254
+
255
+ ```cpp
256
+ // In AIController constructor:
257
+ #include "Perception/AISenseConfig_Sight.h"
258
+ #include "Perception/AISenseConfig_Hearing.h"
259
+ #include "Perception/AISenseConfig_Damage.h"
260
+
261
+ UAIPerceptionComponent* AIP = CreateDefaultSubobject<UAIPerceptionComponent>(TEXT("AIPerception"));
262
+ SetPerceptionComponent(*AIP);
263
+
264
+ UAISenseConfig_Sight* Sight = CreateDefaultSubobject<UAISenseConfig_Sight>(TEXT("Sight"));
265
+ Sight->SightRadius = 2000.f;
266
+ Sight->LoseSightRadius = 2500.f;
267
+ Sight->PeripheralVisionAngleDegrees = 60.f;
268
+ Sight->AutoSuccessRangeFromLastSeenLocation = 400.f;
269
+ Sight->DetectionByAffiliation.bDetectEnemies = true;
270
+ AIP->ConfigureSense(*Sight);
271
+ AIP->SetDominantSense(Sight->GetSenseImplementation());
272
+
273
+ UAISenseConfig_Hearing* Hearing = CreateDefaultSubobject<UAISenseConfig_Hearing>(TEXT("Hearing"));
274
+ Hearing->HearingRange = 3000.f;
275
+ Hearing->DetectionByAffiliation.bDetectEnemies = true;
276
+ Hearing->DetectionByAffiliation.bDetectNeutrals = true;
277
+ AIP->ConfigureSense(*Hearing);
278
+ ```
279
+
280
+ ```cpp
281
+ // Perception handler:
282
+ void AMyAIController::OnTargetPerceptionUpdated(AActor* Actor, FAIStimulus Stimulus)
283
+ {
284
+ UBlackboardComponent* BB = GetBlackboardComponent();
285
+ if (Stimulus.WasSuccessfullySensed())
286
+ {
287
+ BB->SetValueAsObject(TEXT("TargetActor"), Actor);
288
+ BB->SetValueAsVector(TEXT("LastKnownLocation"), Stimulus.StimulusLocation);
289
+ }
290
+ else
291
+ {
292
+ // Lost target — keep last known location for investigation
293
+ BB->SetValueAsVector(TEXT("LastKnownLocation"), Stimulus.StimulusLocation);
294
+ }
295
+ }
296
+
297
+ // Report noise manually (e.g., gunshot):
298
+ UAISense_Hearing::ReportNoiseEvent(GetWorld(), Location, Loudness, Instigator, MaxRange, Tag);
299
+
300
+ // Report damage:
301
+ UAISense_Damage::ReportDamageEvent(GetWorld(), DamagedActor, Instigator, Amount, EventLoc, HitLoc);
302
+ ```
303
+
304
+ ```cpp
305
+ // On perceived actors — add UAIPerceptionStimuliSourceComponent:
306
+ #include "Perception/AIPerceptionStimuliSourceComponent.h"
307
+ UAIPerceptionStimuliSourceComponent* Source =
308
+ CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("StimuliSource"));
309
+ Source->bAutoRegister = true;
310
+ Source->RegisterForSense(TSubclassOf<UAISense>(UAISense_Sight::StaticClass()));
311
+ Source->RegisterForSense(TSubclassOf<UAISense>(UAISense_Hearing::StaticClass()));
312
+ ```
313
+
314
+ ```cpp
315
+ // Query perception state:
316
+ UAIPerceptionComponent* PC = GetAIPerceptionComponent();
317
+ TArray<AActor*> Visible; PC->GetCurrentlyPerceivedActors(UAISense_Sight::StaticClass(), Visible);
318
+ TArray<AActor*> Hostiles; PC->GetPerceivedHostileActors(Hostiles);
319
+ bool bCanSee = PC->HasActiveStimulus(*Target, UAISense::GetSenseID<UAISense_Sight>());
320
+ PC->ForgetActor(Target);
321
+ PC->ForgetAll();
322
+ // Per-actor delegate (shown above):
323
+ // OnTargetPerceptionUpdated — fires once per actor whose perception state changed
324
+ // Batch delegate — fires once per frame with all updated actors:
325
+ // OnPerceptionUpdated — signature: void(const TArray<AActor*>& UpdatedActors)
326
+ // Also: OnTargetPerceptionForgotten, OnTargetPerceptionInfoUpdated
327
+ ```
328
+
329
+ Additional sense configs: `UAISenseConfig_Touch` (fires on physical contact with perceived actor), `UAISense_Team` (propagates enemy awareness across teammates via `IGenericTeamAgentInterface`), `UAISense_Prediction` (predicts future location — call `UAISense_Prediction::RequestPawnPredictionEvent`).
330
+
331
+ ---
332
+
333
+ ## Navigation System
334
+
335
+ ```cpp
336
+ #include "NavigationSystem.h"
337
+ UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld());
338
+
339
+ // Random reachable point
340
+ FNavLocation ResultLoc;
341
+ bool bOk = NavSys->GetRandomReachablePointInRadius(Origin, Radius, ResultLoc);
342
+
343
+ // Project onto NavMesh
344
+ FNavLocation Projected;
345
+ NavSys->ProjectPointToNavigation(WorldLoc, Projected, FVector(500, 500, 500));
346
+
347
+ // Sync path query
348
+ FPathFindingQuery Query; Query.StartLocation = Start; Query.EndLocation = End;
349
+ FPathFindingResult Result = NavSys->FindPathSync(Query);
350
+ if (Result.IsSuccessful()) { TArray<FNavPathPoint>& Pts = Result.Path->GetPathPoints(); }
351
+
352
+ // Async path query
353
+ FNavAgentProperties NavAgent = GetPawn()->GetNavAgentPropertiesRef();
354
+ NavSys->FindPathAsync(NavAgent, Query,
355
+ FNavPathQueryDelegate::CreateUObject(this, &AMyAI::OnPathFound));
356
+
357
+ // Dynamic obstacle on actor:
358
+ #include "NavModifierComponent.h"
359
+ UNavModifierComponent* Mod = CreateDefaultSubobject<UNavModifierComponent>(TEXT("NavMod"));
360
+ Mod->AreaClass = UNavArea_Obstacle::StaticClass();
361
+
362
+ // Custom nav filter (prefer certain areas):
363
+ UCLASS() class UMyNavFilter : public UNavigationQueryFilter { ... };
364
+ MoveToActor(Target, -1.f, true, true, true, UMyNavFilter::StaticClass());
365
+
366
+ // Runtime NavMesh rebuild (after procedural generation):
367
+ NavSys->Build();
368
+
369
+ // Access RecastNavMesh for agent config (mirrors Project Settings > Navigation)
370
+ ARecastNavMesh* RNM = Cast<ARecastNavMesh>(NavSys->GetDefaultNavDataInstance());
371
+ // Key properties: AgentRadius, AgentHeight, AgentMaxStepHeight, AgentMaxSlope
372
+ // ANavMeshBoundsVolume must exist in level — without it, no tiles generate.
373
+ // For streaming/open-world: use UNavigationInvokerComponent on AI pawns instead.
374
+ ```
375
+
376
+ **Off-mesh links**: Use `ANavLinkProxy` in-level, or implement `INavLinkCustomInterface` for traversal callbacks.
377
+
378
+ ---
379
+
380
+ ## EQS (Environment Query System)
381
+
382
+ ```cpp
383
+ #include "EnvironmentQuery/EnvQueryManager.h"
384
+
385
+ UPROPERTY(EditDefaultsOnly) TObjectPtr<UEnvQuery> FindCoverQuery;
386
+
387
+ void AMyAIController::RunCoverQuery()
388
+ {
389
+ FEnvQueryRequest Request(FindCoverQuery, this);
390
+ Request.Execute(EEnvQueryRunMode::SingleResult,
391
+ FQueryFinishedSignature::CreateUObject(this, &AMyAIController::OnCoverDone));
392
+ }
393
+
394
+ void AMyAIController::OnCoverDone(TSharedPtr<FEnvQueryResult> Result)
395
+ {
396
+ if (Result.IsValid() && Result->IsSuccessful())
397
+ GetBlackboardComponent()->SetValueAsVector(TEXT("CoverLocation"),
398
+ Result->GetItemAsLocation(0));
399
+ }
400
+ ```
401
+
402
+ **Run modes**: `SingleResult` (cheapest), `RandomBest5Pct`, `RandomBest25Pct`, `AllMatching`
403
+
404
+ **BTTask_RunEQSQuery**: set `EQSRequest.QueryTemplate`, `BlackboardKey`, `RunMode`. Async lifecycle managed via `FBTEnvQueryTaskMemory.RequestID`.
405
+
406
+ **Custom context** (resolve BB actor for use in tests):
407
+
408
+ ```cpp
409
+ UCLASS()
410
+ class UEnvQueryContext_Enemy : public UEnvQueryContext
411
+ {
412
+ GENERATED_BODY()
413
+ virtual void ProvideContext(FEnvQueryInstance& QI, FEnvQueryContextData& CD) const override
414
+ {
415
+ AAIController* C = Cast<AAIController>(Cast<APawn>(QI.Owner.Get())->GetController());
416
+ AActor* Enemy = Cast<AActor>(C->GetBlackboardComponent()->GetValueAsObject(TEXT("TargetActor")));
417
+ if (IsValid(Enemy)) UEnvQueryItemType_Actor::SetContextHelper(CD, Enemy);
418
+ }
419
+ };
420
+ ```
421
+
422
+ See `references/eqs-reference.md` for generator and test configurations.
423
+
424
+ ---
425
+
426
+ ## State Trees (UE 5.1+)
427
+
428
+ Use State Trees for simpler state machines, designer-friendly workflows, and Smart Object integration. Use Behavior Trees for complex reactive combat with priority-based interrupts.
429
+
430
+ ```cpp
431
+ // Build.cs: "GameplayStateTreeModule"
432
+ #include "Components/StateTreeComponent.h"
433
+
434
+ UCLASS()
435
+ class AMyNPC : public ACharacter
436
+ {
437
+ GENERATED_BODY()
438
+ UPROPERTY(VisibleAnywhere) TObjectPtr<UStateTreeComponent> StateTreeComp;
439
+ };
440
+ AMyNPC::AMyNPC() { StateTreeComp = CreateDefaultSubobject<UStateTreeComponent>(TEXT("StateTree")); }
441
+ void AMyNPC::BeginPlay() { Super::BeginPlay(); StateTreeComp->StartLogic(); }
442
+ ```
443
+
444
+ State Tree tasks use `FStateTreeTaskBase` + `FInstanceDataType`. Override `EnterState`, `Tick`, `ExitState`.
445
+
446
+ State Tree **evaluators** (`FStateTreeEvaluatorBase`) run persistently across all active states — use them for shared context data (nearest enemy, threat level) that multiple states read, analogous to BT services.
447
+
448
+ ---
449
+
450
+ ## Smart Objects
451
+
452
+ Add `USmartObjectComponent` to world actors (set `SmartObjectDefinition`). AI interacts via `USmartObjectSubsystem`:
453
+
454
+ ```cpp
455
+ // Build.cs: "SmartObjectsModule"
456
+ #include "SmartObjectSubsystem.h"
457
+ USmartObjectSubsystem* SOS = USmartObjectSubsystem::GetCurrent(GetWorld());
458
+ FSmartObjectRequestFilter Filter;
459
+ FSmartObjectRequest Req(FBox(Origin, Origin).ExpandBy(500.f), Filter);
460
+ FSmartObjectRequestResult Res = SOS->FindSmartObject(Req);
461
+ if (Res.IsValid())
462
+ {
463
+ FSmartObjectClaimHandle Handle = SOS->MarkSlotAsClaimed(Res.SlotHandle, ESmartObjectClaimPriority::Normal);
464
+ // ... use slot, then:
465
+ SOS->MarkSlotAsFree(Handle);
466
+ }
467
+ ```
468
+
469
+ ---
470
+
471
+ ## Common Mistakes
472
+
473
+ **Polling instead of event-driven**: Do not check per-frame in `TickTask` if a `BTDecorator_Blackboard` observer abort or `WaitForMessage` achieves the same result.
474
+
475
+ **Overcomplicated BTs**: Flatten needless nesting. Use services for periodic knowledge updates at `Interval >= 0.5s`. Gate expensive EQS with `BTDecorator_Cooldown`.
476
+
477
+ **NavMesh gaps**: Always place `ANavMeshBoundsVolume`. For streaming/procedural levels, call `NavSys->Build()` after generation or use `UNavigationInvokerComponent` on AI pawns.
478
+
479
+ **Server vs client**: `AAIController` only exists on the server. Replicate AI state via Pawn replicated properties, not through the controller. BT Blackboard is not replicated.
480
+
481
+ **Large worlds**: Use hierarchical NavMesh (RecastNavMesh actor settings). Set EQS max parallel queries per frame in Project Settings > AI > EQS.
482
+
483
+ ---
484
+
485
+ ## Edge Cases
486
+
487
+ - **Flying/swimming**: Set `FNavAgentProperties::bCanFly`/`bCanSwim`; assign a `UNavArea` subclass with matching `SupportedAgents` flags. Custom `UNavArea` subclasses define traversal cost and area flags — create one per movement domain (e.g., `UNavArea_Water` with high cost for ground agents, zero for swimmers). Apply via NavModifierVolumes or NavMesh generation settings.
488
+ - **Dedicated server**: Sight perception uses collision traces, not rendering — works fine. Guard non-server code with `HasAuthority()`.
489
+ - **AI with GAS**: BT tasks request ability activation; abilities manage their own latent logic.
490
+
491
+ ---
492
+
493
+ ## Related Skills
494
+
495
+ - `/ue-actor-component-architecture` — component setup patterns for AI
496
+ - `/ue-gameplay-framework` — GameMode AI spawning, controller/pawn relationships
497
+ - `/ue-cpp-foundations` — delegates, subsystems, UObject patterns
498
+ - `/ue-gameplay-abilities` — GAS + AI integration
499
+ - `/ue-naming-conventions` — asset prefixes and Content folder layout for these assets
500
+
501
+ ## Reference Files
502
+
503
+ - `references/behavior-tree-patterns.md` — patrol, combat, flee, investigate, squad BT patterns
504
+ - `references/eqs-reference.md` — generator and test configurations for spatial queries
505
+ - `references/behavior-tree-workflow.md` — editor-side authoring flow: composites, decorators, services, AIController wiring
506
+ - `references/custom-bttask.md` — writing a custom `BTTask` node, including the latent-task pattern
507
+
508
+ ---
509
+
510
+ ## Checklist
511
+
512
+ Validate before considering the work done:
513
+
514
+ - [ ] Behavior tree decorators observe blackboard keys instead of polling in tasks
515
+ - [ ] Perception configuration matches the intended detection ranges and teams
516
+ - [ ] NavMesh generation settings cover the agent sizes actually used
517
+ - [ ] Conventions in `docs/unreal/project-context.md` were read and followed
518
+ - [ ] The Common Mistakes section of this skill was reviewed against the change
519
+
520
+ **PASS** — every item holds. **CONCERNS** — the work stands but at least one item is unresolved and worth flagging to the user. **FAIL** — an item is violated in a way that breaks correctness, replication or the frame budget; fix it before handing the work over.
521
+
522
+ ## Next Steps
523
+
524
+ - `/ue-state-trees` — consider StateTree if the tree is getting unwieldy
525
+ - `/code-review` — architectural and quality review of the change
526
+ - `/commit` — conventional commit once the change is verified