claude-code-arcane 2.3.0 → 2.4.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 (103) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +54 -6
  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 +5 -0
  7. package/docs/SKILLS-CATALOG.md +42 -3
  8. package/docs/USER-GUIDE.md +2 -2
  9. package/hooks/detect-unreal.sh +90 -0
  10. package/package.json +1 -1
  11. package/profiles/unreal-dev.yaml +97 -0
  12. package/rules/gamedev/unreal-code.md +125 -0
  13. package/skills/install-mcp/SKILL.md +135 -46
  14. package/skills/install-mcp/references/unreal-operations.md +55 -0
  15. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  16. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  17. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  18. package/skills/ue-ai-navigation/SKILL.md +526 -0
  19. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  20. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  21. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  22. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  23. package/skills/ue-animation-system/SKILL.md +521 -0
  24. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  25. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  26. package/skills/ue-async-threading/SKILL.md +433 -0
  27. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  28. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  29. package/skills/ue-audio-system/SKILL.md +527 -0
  30. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  31. package/skills/ue-blueprints/SKILL.md +134 -0
  32. package/skills/ue-blueprints/references/communication.md +82 -0
  33. package/skills/ue-character-movement/SKILL.md +475 -0
  34. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  35. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  36. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  37. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  38. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  39. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  40. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  41. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  42. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  43. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  44. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  45. package/skills/ue-editor-tools/SKILL.md +523 -0
  46. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  47. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  48. package/skills/ue-game-features/SKILL.md +477 -0
  49. package/skills/ue-game-features/references/experience-system.md +222 -0
  50. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  51. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  52. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  53. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  54. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  55. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  56. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  57. package/skills/ue-input-system/SKILL.md +394 -0
  58. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  59. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  60. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  61. package/skills/ue-mass-entity/SKILL.md +476 -0
  62. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  63. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  64. package/skills/ue-materials-rendering/SKILL.md +526 -0
  65. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  66. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  67. package/skills/ue-module-build-system/SKILL.md +487 -0
  68. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  69. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  70. package/skills/ue-naming-conventions/SKILL.md +142 -0
  71. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  72. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  73. package/skills/ue-networking-replication/SKILL.md +452 -0
  74. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  75. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  76. package/skills/ue-niagara-effects/SKILL.md +531 -0
  77. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  78. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  79. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  80. package/skills/ue-packaging/SKILL.md +116 -0
  81. package/skills/ue-physics-collision/SKILL.md +423 -0
  82. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  83. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  84. package/skills/ue-procedural-generation/SKILL.md +505 -0
  85. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  86. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  87. package/skills/ue-project-context/SKILL.md +305 -0
  88. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  89. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  90. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  91. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  92. package/skills/ue-state-trees/SKILL.md +489 -0
  93. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  94. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  95. package/skills/ue-testing-debugging/SKILL.md +520 -0
  96. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  97. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  98. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  99. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  100. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  101. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  102. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  103. package/templates/gamedev/unreal-project-context.md +107 -0
@@ -0,0 +1,497 @@
1
+ # Delegate Patterns Reference
2
+
3
+ Complete reference for Unreal Engine's delegate system: declaration macros, binding methods, and invocation patterns. Source: `Engine/Source/Runtime/Core/Public/Delegates/DelegateCombinations.h` and `Delegate.h`.
4
+
5
+ ---
6
+
7
+ ## Delegate Type Overview
8
+
9
+ | Type | Macro Family | Bindings | Serializable | Blueprint | Use Case |
10
+ |------|-------------|----------|--------------|-----------|----------|
11
+ | Single static | `DECLARE_DELEGATE` | 1 | No | No | Internal single-owner callbacks |
12
+ | Multicast static | `DECLARE_MULTICAST_DELEGATE` | N | No | No | Internal multi-listener events |
13
+ | Thread-safe multicast | `DECLARE_TS_MULTICAST_DELEGATE` | N | No | No | Cross-thread event broadcasting |
14
+ | Dynamic single | `DECLARE_DYNAMIC_DELEGATE` | 1 | Yes | Yes | Blueprint-assignable single callback |
15
+ | Dynamic multicast | `DECLARE_DYNAMIC_MULTICAST_DELEGATE` | N | Yes | Yes | Blueprint-bindable events (most common for gameplay) |
16
+ | Return-value single | `DECLARE_DELEGATE_RetVal` | 1 | No | No | Query callbacks with a return value |
17
+
18
+ ---
19
+
20
+ ## Declaration Macros
21
+
22
+ All delegate type declarations go at **file scope** (outside classes), except `DECLARE_DYNAMIC_*` which should also be at file scope before the UCLASS that uses them.
23
+
24
+ ### Single Delegate (No Parameters)
25
+
26
+ ```cpp
27
+ DECLARE_DELEGATE(FOnGamePaused);
28
+ ```
29
+
30
+ ### Single Delegate with Parameters
31
+
32
+ ```cpp
33
+ // One parameter
34
+ DECLARE_DELEGATE_OneParam(FOnItemCollected, AActor* /* Item */);
35
+
36
+ // Two parameters
37
+ DECLARE_DELEGATE_TwoParams(FOnDamageDealt, AActor* /* Target */, float /* Amount */);
38
+
39
+ // Three parameters
40
+ DECLARE_DELEGATE_ThreeParams(FOnAbilityUsed, FName /* AbilityID */, int32 /* Level */, float /* Cooldown */);
41
+
42
+ // Up to nine parameters supported
43
+ DECLARE_DELEGATE_FourParams(FOnWeaponFired, FVector /* Origin */, FVector /* Direction */, float /* Damage */, AActor* /* Shooter */);
44
+ ```
45
+
46
+ ### Single Delegate with Return Value
47
+
48
+ ```cpp
49
+ DECLARE_DELEGATE_RetVal(bool, FOnShouldSpawn);
50
+ DECLARE_DELEGATE_RetVal_OneParam(float, FOnCalculateDamage, float /* BaseDamage */);
51
+ DECLARE_DELEGATE_RetVal_TwoParams(AActor*, FOnSelectTarget, FVector /* Origin */, float /* Range */);
52
+ ```
53
+
54
+ ### Multicast Delegate
55
+
56
+ ```cpp
57
+ DECLARE_MULTICAST_DELEGATE(FOnLevelLoaded);
58
+ DECLARE_MULTICAST_DELEGATE_OneParam(FOnActorSpawned, AActor* /* SpawnedActor */);
59
+ DECLARE_MULTICAST_DELEGATE_TwoParams(FOnHealthChanged, float /* Current */, float /* Max */);
60
+ DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameEvent, FName /* EventID */, UObject* /* Instigator */, const FString& /* Payload */);
61
+ ```
62
+
63
+ ### Thread-Safe Multicast
64
+
65
+ ```cpp
66
+ // Use when broadcasting from non-game threads
67
+ DECLARE_TS_MULTICAST_DELEGATE_OneParam(FOnAsyncDataReady, const TArray<uint8>& /* Data */);
68
+ ```
69
+
70
+ ### Dynamic Single Delegate
71
+
72
+ ```cpp
73
+ // Saveable, can be assigned in Blueprint
74
+ // Param names are required (used as Blueprint pin labels)
75
+ DECLARE_DYNAMIC_DELEGATE(FOnSimpleEvent);
76
+ DECLARE_DYNAMIC_DELEGATE_OneParam(FOnActorDestroyed, AActor*, DestroyedActor);
77
+ DECLARE_DYNAMIC_DELEGATE_TwoParams(FOnTransformChanged, FVector, NewLocation, FRotator, NewRotation);
78
+
79
+ // With return value
80
+ DECLARE_DYNAMIC_DELEGATE_RetVal(bool, FOnValidationCheck);
81
+ DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(float, FOnGetModifiedDamage, float, BaseDamage);
82
+ ```
83
+
84
+ ### Dynamic Multicast Delegate
85
+
86
+ ```cpp
87
+ // Most common for UPROPERTY(BlueprintAssignable) events
88
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameStarted);
89
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnScoreChanged, int32, NewScore);
90
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnPlayerDied, APlayerController*, PlayerController, AActor*, KillerActor);
91
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(
92
+ FOnAbilityActivated,
93
+ FName, AbilityID,
94
+ int32, Level,
95
+ APlayerState*, PlayerState);
96
+
97
+ // Four params
98
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(
99
+ FOnHitResult,
100
+ FVector, HitLocation,
101
+ FVector, HitNormal,
102
+ float, Damage,
103
+ AActor*, HitActor);
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Declaring in a UCLASS
109
+
110
+ ```cpp
111
+ // Forward-declare or include delegate declarations before the class
112
+
113
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnHealthChanged, float, CurrentHealth, float, MaxHealth);
114
+ DECLARE_MULTICAST_DELEGATE_OneParam(FOnDeathNative, AActor* /* DeadActor */);
115
+
116
+ UCLASS(Blueprintable)
117
+ class MYGAME_API AMyCharacter : public ACharacter
118
+ {
119
+ GENERATED_BODY()
120
+
121
+ public:
122
+ // BlueprintAssignable — Blueprint can bind to this in event graph
123
+ UPROPERTY(BlueprintAssignable, Category="Events")
124
+ FOnHealthChanged OnHealthChanged;
125
+
126
+ // BlueprintCallable on a delegate property — Blueprint can call Broadcast
127
+ UPROPERTY(BlueprintAssignable, BlueprintCallable, Category="Events")
128
+ FOnHealthChanged OnHealthChangedCallable;
129
+
130
+ // Native-only multicast (no UPROPERTY needed, but no GC protection either)
131
+ FOnDeathNative OnDeathNative;
132
+ };
133
+ ```
134
+
135
+ ---
136
+
137
+ ## Binding Methods
138
+
139
+ ### Single Static Delegate Binding
140
+
141
+ ```cpp
142
+ FOnItemCollected Delegate;
143
+
144
+ // Bind to a UObject member function (safe: auto-unbinds on UObject destruction)
145
+ Delegate.BindUObject(this, &AMyCharacter::HandleItemCollected);
146
+
147
+ // Bind to a raw C++ pointer (unsafe: does NOT unbind automatically)
148
+ Delegate.BindRaw(RawPtr, &FMyHelper::HandleItem);
149
+
150
+ // Bind to a static function (no instance)
151
+ Delegate.BindStatic(&UMyLibrary::StaticHandleItem);
152
+
153
+ // Bind to a TSharedPtr owner (safe: weak-ref checked on invocation)
154
+ Delegate.BindSP(SharedOwner, &FMyOwner::Handle);
155
+
156
+ // Bind to a lambda
157
+ Delegate.BindLambda([this](AActor* Item)
158
+ {
159
+ UE_LOG(LogMyGame, Log, TEXT("Collected: %s"), *Item->GetName());
160
+ });
161
+
162
+ // Bind with extra payload parameters (up to 4 bound "payload" vars)
163
+ float ScoreBonus = 1.5f;
164
+ Delegate.BindUObject(this, &AMyCharacter::HandleItemWithBonus, ScoreBonus);
165
+
166
+ // Check binding state
167
+ if (Delegate.IsBound())
168
+ {
169
+ Delegate.Execute(SomeActor);
170
+ }
171
+
172
+ // ExecuteIfBound — no-op if not bound (use for optional callbacks)
173
+ Delegate.ExecuteIfBound(SomeActor);
174
+
175
+ // Unbind
176
+ Delegate.Unbind();
177
+ ```
178
+
179
+ ### Multicast Static Delegate Binding
180
+
181
+ ```cpp
182
+ FOnHealthChanged HealthDelegate;
183
+
184
+ // Add binding — returns a handle for later removal
185
+ FDelegateHandle Handle = HealthDelegate.AddUObject(this, &AMyHUD::OnHealthChanged);
186
+
187
+ // Lambda binding
188
+ FDelegateHandle LambdaHandle = HealthDelegate.AddLambda([](float Current, float Max)
189
+ {
190
+ // Update UI
191
+ });
192
+
193
+ // Raw pointer binding
194
+ FDelegateHandle RawHandle = HealthDelegate.AddRaw(RawPtr, &FMyClass::Handle);
195
+
196
+ // SP binding
197
+ FDelegateHandle SpHandle = HealthDelegate.AddSP(SharedOwner, &FMyClass::Handle);
198
+
199
+ // Broadcast — calls all bound functions
200
+ HealthDelegate.Broadcast(75.f, 100.f);
201
+
202
+ // Remove by handle (precise)
203
+ HealthDelegate.Remove(Handle);
204
+ HealthDelegate.Remove(LambdaHandle);
205
+
206
+ // Remove by UObject — removes all bindings from that object
207
+ HealthDelegate.RemoveAll(this);
208
+
209
+ // Check if any bindings exist
210
+ bool bHasListeners = HealthDelegate.IsBound();
211
+ ```
212
+
213
+ ### Dynamic Single Delegate Binding
214
+
215
+ ```cpp
216
+ FOnActorDestroyed Delegate;
217
+
218
+ // Bind to a UFUNCTION on a UObject (the function MUST be a UFUNCTION)
219
+ Delegate.BindDynamic(this, &AMyCharacter::HandleActorDestroyed);
220
+
221
+ // Check
222
+ bool bBound = Delegate.IsBound();
223
+
224
+ // Execute
225
+ Delegate.ExecuteIfBound(SomeActor);
226
+
227
+ // Unbind
228
+ Delegate.Unbind();
229
+ ```
230
+
231
+ ### Dynamic Multicast Delegate Binding
232
+
233
+ ```cpp
234
+ // In UCLASS that declares OnHealthChanged:
235
+ OnHealthChanged.AddDynamic(this, &AMyHUD::HandleHealthChanged);
236
+
237
+ // Remove specific binding
238
+ OnHealthChanged.RemoveDynamic(this, &AMyHUD::HandleHealthChanged);
239
+
240
+ // Remove all bindings from an object
241
+ OnHealthChanged.RemoveAll(this);
242
+
243
+ // Broadcast
244
+ OnHealthChanged.Broadcast(NewHealth, MaxHealth);
245
+
246
+ // Check
247
+ bool bHasBindings = OnHealthChanged.IsBound();
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Complete Example: Full Delegate Workflow
253
+
254
+ ### Component-based health with events
255
+
256
+ ```cpp
257
+ // HealthComponent.h
258
+
259
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnHealthChangedSignature, float, CurrentHealth, float, MaxHealth);
260
+ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDeathSignature, AActor*, KillerActor);
261
+ DECLARE_MULTICAST_DELEGATE_OneParam(FOnHealthChangedNative, float /* NewHealthFraction */);
262
+
263
+ UCLASS(ClassGroup=Combat, meta=(BlueprintSpawnableComponent))
264
+ class MYGAME_API UHealthComponent : public UActorComponent
265
+ {
266
+ GENERATED_BODY()
267
+
268
+ public:
269
+ // Blueprint-assignable events
270
+ UPROPERTY(BlueprintAssignable, Category="Health")
271
+ FOnHealthChangedSignature OnHealthChanged;
272
+
273
+ UPROPERTY(BlueprintAssignable, Category="Health")
274
+ FOnDeathSignature OnDeath;
275
+
276
+ // Native-only event for C++ subsystems (faster, no reflection overhead)
277
+ FOnHealthChangedNative OnHealthChangedNative;
278
+
279
+ UFUNCTION(BlueprintCallable, Category="Health")
280
+ void ApplyDamage(float DamageAmount, AActor* DamageInstigator);
281
+
282
+ UFUNCTION(BlueprintPure, Category="Health")
283
+ float GetHealthFraction() const { return CurrentHealth / MaxHealth; }
284
+
285
+ private:
286
+ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Health",
287
+ meta=(AllowPrivateAccess="true", ClampMin="1.0"))
288
+ float MaxHealth = 100.f;
289
+
290
+ UPROPERTY(Transient)
291
+ float CurrentHealth;
292
+
293
+ virtual void BeginPlay() override;
294
+ };
295
+ ```
296
+
297
+ ```cpp
298
+ // HealthComponent.cpp
299
+
300
+ void UHealthComponent::BeginPlay()
301
+ {
302
+ Super::BeginPlay();
303
+ CurrentHealth = MaxHealth;
304
+ }
305
+
306
+ void UHealthComponent::ApplyDamage(float DamageAmount, AActor* DamageInstigator)
307
+ {
308
+ if (CurrentHealth <= 0.f) return;
309
+
310
+ CurrentHealth = FMath::Clamp(CurrentHealth - DamageAmount, 0.f, MaxHealth);
311
+
312
+ // Fire both native and dynamic events
313
+ OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
314
+ OnHealthChangedNative.Broadcast(CurrentHealth / MaxHealth);
315
+
316
+ if (CurrentHealth <= 0.f)
317
+ {
318
+ OnDeath.Broadcast(DamageInstigator);
319
+ }
320
+ }
321
+ ```
322
+
323
+ ```cpp
324
+ // MyCharacter.cpp — binding to the component's delegates
325
+
326
+ void AMyCharacter::BeginPlay()
327
+ {
328
+ Super::BeginPlay();
329
+
330
+ UHealthComponent* HC = FindComponentByClass<UHealthComponent>();
331
+ if (HC)
332
+ {
333
+ // Dynamic: Blueprint can also bind this in its event graph
334
+ HC->OnHealthChanged.AddDynamic(this, &AMyCharacter::HandleHealthChanged);
335
+ HC->OnDeath.AddDynamic(this, &AMyCharacter::HandleDeath);
336
+
337
+ // Native: C++ subsystem listens too
338
+ HC->OnHealthChangedNative.AddUObject(this, &AMyCharacter::HandleHealthChangedNative);
339
+ }
340
+ }
341
+
342
+ void AMyCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
343
+ {
344
+ UHealthComponent* HC = FindComponentByClass<UHealthComponent>();
345
+ if (HC)
346
+ {
347
+ HC->OnHealthChanged.RemoveDynamic(this, &AMyCharacter::HandleHealthChanged);
348
+ HC->OnDeath.RemoveDynamic(this, &AMyCharacter::HandleDeath);
349
+ HC->OnHealthChangedNative.RemoveAll(this);
350
+ }
351
+ Super::EndPlay(EndPlayReason);
352
+ }
353
+
354
+ void AMyCharacter::HandleHealthChanged(float CurrentHealth, float MaxHealth)
355
+ {
356
+ UE_LOG(LogMyGame, Log, TEXT("[%s] Health: %.1f / %.1f"), *GetName(), CurrentHealth, MaxHealth);
357
+ }
358
+
359
+ void AMyCharacter::HandleDeath(AActor* KillerActor)
360
+ {
361
+ UE_LOG(LogMyGame, Warning, TEXT("[%s] Died. Killer: %s"), *GetName(),
362
+ KillerActor ? *KillerActor->GetName() : TEXT("Unknown"));
363
+ }
364
+
365
+ void AMyCharacter::HandleHealthChangedNative(float HealthFraction)
366
+ {
367
+ // Update UI or animation state without Blueprint overhead
368
+ }
369
+ ```
370
+
371
+ ---
372
+
373
+ ## Binding Method Quick Reference
374
+
375
+ | Method | Owner Safety | Auto-Unbind | Use With |
376
+ |--------|-------------|-------------|----------|
377
+ | `BindUObject` / `AddUObject` | Yes (weak ref) | On UObject destroy | UObject-derived classes |
378
+ | `BindSP` / `AddSP` | Yes (weak ptr) | On TSharedPtr destruction | Non-UObject with TSharedPtr |
379
+ | `BindRaw` / `AddRaw` | No | Never | Raw pointers (manual unbind required) |
380
+ | `BindStatic` / `AddStatic` | N/A | Never | Global/static functions |
381
+ | `BindLambda` / `AddLambda` | No (capture manually) | Never | Short-lived, inline callbacks |
382
+ | `BindDynamic` / `AddDynamic` | Yes (UFUNCTION) | On UObject destroy | Dynamic delegates only |
383
+
384
+ > **Safety rule:** Always `RemoveAll(this)` in `EndPlay` or destructor when using `AddLambda` or `AddRaw` to avoid calling into destroyed objects.
385
+
386
+ ---
387
+
388
+ ## Common Mistakes
389
+
390
+ **Calling Execute() on an unbound delegate:**
391
+ ```cpp
392
+ // BAD — asserts in Debug if not bound
393
+ PickupDelegate.Execute(SomeActor);
394
+
395
+ // GOOD
396
+ PickupDelegate.ExecuteIfBound(SomeActor);
397
+ // Or:
398
+ if (PickupDelegate.IsBound()) { PickupDelegate.Execute(SomeActor); }
399
+ ```
400
+
401
+ **Binding lambda to multicast without storing the handle:**
402
+ ```cpp
403
+ // BAD — cannot be removed later
404
+ OnHealthChanged.AddLambda([this](float C, float M) { ... });
405
+
406
+ // GOOD — store the handle
407
+ FDelegateHandle Handle = OnHealthChanged.AddLambda([this](float C, float M) { ... });
408
+ // Later:
409
+ OnHealthChanged.Remove(Handle);
410
+ ```
411
+
412
+ **Using AddDynamic with a non-UFUNCTION:**
413
+ ```cpp
414
+ // BAD — compile error or undefined behavior
415
+ OnHealthChanged.AddDynamic(this, &AMyCharacter::NonUFunction);
416
+
417
+ // GOOD — the function must have UFUNCTION()
418
+ UFUNCTION()
419
+ void HandleHealthChanged(float Current, float Max);
420
+ ```
421
+
422
+ **Forgetting to RemoveDynamic in EndPlay:**
423
+ ```cpp
424
+ // BAD — dangling binding, crash when broadcast fires after object destruction
425
+ void AMyActor::BeginPlay()
426
+ {
427
+ Super::BeginPlay();
428
+ OtherActor->OnEvent.AddDynamic(this, &AMyActor::HandleEvent);
429
+ // Never removed!
430
+ }
431
+
432
+ // GOOD
433
+ void AMyActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
434
+ {
435
+ if (IsValid(OtherActor))
436
+ {
437
+ OtherActor->OnEvent.RemoveDynamic(this, &AMyActor::HandleEvent);
438
+ }
439
+ Super::EndPlay(EndPlayReason);
440
+ }
441
+ ```
442
+
443
+ **Dynamic multicast delegate not in UPROPERTY:**
444
+ ```cpp
445
+ // BAD — not serialized; Blueprint bindings lost between sessions
446
+ FOnScoreChanged OnScoreChanged;
447
+
448
+ // GOOD
449
+ UPROPERTY(BlueprintAssignable, Category="Events")
450
+ FOnScoreChanged OnScoreChanged;
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Return-Value Delegate Pattern
456
+
457
+ Single static delegates support return values. Multicast and dynamic delegates do not (which binding's return value would you use?).
458
+
459
+ ```cpp
460
+ DECLARE_DELEGATE_RetVal_OneParam(bool, FOnValidatePickup, AActor* /* Item */);
461
+
462
+ FOnValidatePickup ValidationDelegate;
463
+
464
+ ValidationDelegate.BindLambda([](AActor* Item) -> bool
465
+ {
466
+ return IsValid(Item) && Item->ActorHasTag(FName("Pickup"));
467
+ });
468
+
469
+ if (ValidationDelegate.IsBound())
470
+ {
471
+ bool bValid = ValidationDelegate.Execute(PotentialPickup);
472
+ if (bValid) { CollectItem(PotentialPickup); }
473
+ }
474
+ ```
475
+
476
+ ---
477
+
478
+ ## Payload Parameters (Bound Extras)
479
+
480
+ Single delegate bind methods accept up to 4 "payload" values that are forwarded after the delegate's declared parameters.
481
+
482
+ ```cpp
483
+ DECLARE_DELEGATE_OneParam(FOnItemPickedUp, AActor* /* Item */);
484
+
485
+ float BonusMultiplier = 1.5f;
486
+ FName PickupSound = FName("SFX_Pickup");
487
+
488
+ // BonusMultiplier and PickupSound are captured at bind time
489
+ FOnItemPickedUp Delegate;
490
+ Delegate.BindUObject(this, &AMyCharacter::HandlePickupWithExtras, BonusMultiplier, PickupSound);
491
+
492
+ // The bound function signature must match declared params + payload params
493
+ void AMyCharacter::HandlePickupWithExtras(AActor* Item, float Bonus, FName Sound)
494
+ {
495
+ // Bonus == 1.5f, Sound == "SFX_Pickup"
496
+ }
497
+ ```
@@ -0,0 +1,136 @@
1
+ # Gameplay Classes in C++ — Editor-Side Workflow
2
+
3
+ > Adapted from the `unreal-cpp-gameplay` skill in [awesome-gamedev-agent-skills](https://github.com/gamedev-skills/awesome-gamedev-agent-skills) (Apache-2.0). Cross-skill references were remapped to Arcane slash commands.
4
+
5
+ Write correct UE5 gameplay C++: the reflection macros that connect C++ to the editor and
6
+ Blueprints, the Gameplay Framework class roles, and module dependencies. Targets **UE 5.8**.
7
+
8
+ ## When to use
9
+
10
+ - Use when creating C++ gameplay classes (`AActor`, `APawn`, `ACharacter`, `AGameModeBase`,
11
+ `UActorComponent`), exposing properties/functions with `UPROPERTY`/`UFUNCTION`, setting up a
12
+ GameMode's default classes, or adding a module dependency in `*.Build.cs`.
13
+ - Use when the project has a `Source/` tree with `*.h`/`*.cpp` using `UCLASS`, and `*.Build.cs`.
14
+
15
+ **When *not* to use:** designer-facing visual logic → `/ue-blueprints`. Player input
16
+ binding details → `/ue-input-system`. AI logic → `/ue-ai-navigation`. This skill owns
17
+ the C++ class/reflection foundation those build on.
18
+
19
+ ## Core workflow
20
+
21
+ 1. **Name with the right prefix.** `A` = Actor-derived, `U` = `UObject`/component-derived,
22
+ `F` = plain struct, `E` = enum, `I` = interface. The prefix must match the base class.
23
+ 2. **Declare the class with reflection macros.** `UCLASS()` above the class, `GENERATED_BODY()`
24
+ as the first line in the body, and `#include "ClassName.generated.h"` as the **last**
25
+ include in the header.
26
+ 3. **Expose data with `UPROPERTY`** (editor/Blueprint visibility *and* garbage-collection
27
+ tracking) and behaviour with `UFUNCTION` (`BlueprintCallable`, etc.).
28
+ 4. **Create components in the constructor** with `CreateDefaultSubobject<T>(TEXT("Name"))` and
29
+ set the `RootComponent`.
30
+ 5. **Know the framework roles:** `AGameModeBase` sets the rules + default classes; `APawn`/
31
+ `ACharacter` is the controllable body; `APlayerController` is the player's will;
32
+ `UActorComponent` is reusable behaviour.
33
+ 6. **Add module dependencies** to `*.Build.cs` (e.g. `EnhancedInput`) or unresolved-symbol
34
+ link errors follow.
35
+ 7. **Verify** by compiling (Live Coding `Ctrl+Alt+F11` for function bodies; full rebuild for
36
+ header/UPROPERTY changes) and checking the class/properties appear in the editor.
37
+
38
+ ## Patterns
39
+
40
+ ### 1. Minimal Actor class (header + source)
41
+
42
+ ```cpp
43
+ // Pickup.h
44
+ #pragma once
45
+ #include "CoreMinimal.h"
46
+ #include "GameFramework/Actor.h"
47
+ #include "Pickup.generated.h" // MUST be the last include
48
+
49
+ UCLASS()
50
+ class MYGAME_API APickup : public AActor // MYGAME_API = your module's export macro
51
+ {
52
+ GENERATED_BODY()
53
+ public:
54
+ APickup();
55
+
56
+ // EditAnywhere = tweak per-instance & on the CDO; BlueprintReadWrite = BP get/set.
57
+ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Pickup")
58
+ int32 ScoreValue = 10;
59
+
60
+ // UPROPERTY on a UObject* pointer is what keeps it from being garbage-collected.
61
+ UPROPERTY(VisibleAnywhere)
62
+ TObjectPtr<UStaticMeshComponent> Mesh; // UE5: TObjectPtr instead of raw UStaticMeshComponent*
63
+
64
+ UFUNCTION(BlueprintCallable, Category = "Pickup")
65
+ void Collect();
66
+
67
+ protected:
68
+ virtual void BeginPlay() override;
69
+ };
70
+ ```
71
+
72
+ ```cpp
73
+ // Pickup.cpp
74
+ #include "Pickup.h"
75
+ #include "Components/StaticMeshComponent.h"
76
+
77
+ APickup::APickup()
78
+ {
79
+ Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
80
+ RootComponent = Mesh; // the mesh is this actor's root
81
+ }
82
+
83
+ void APickup::BeginPlay() { Super::BeginPlay(); } // always call Super
84
+ void APickup::Collect() { Destroy(); }
85
+ ```
86
+
87
+ ### 2. GameMode wiring its default classes
88
+
89
+ ```cpp
90
+ // MyGameMode.cpp — set in the constructor so the engine spawns your classes.
91
+ AMyGameMode::AMyGameMode()
92
+ {
93
+ DefaultPawnClass = AMyCharacter::StaticClass();
94
+ PlayerControllerClass = AMyPlayerController::StaticClass();
95
+ }
96
+ ```
97
+
98
+ ### 3. Module dependency in Build.cs
99
+
100
+ ```csharp
101
+ // MyGame.Build.cs
102
+ PublicDependencyModuleNames.AddRange(new string[]
103
+ {
104
+ "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput"
105
+ });
106
+ ```
107
+
108
+ ## Pitfalls
109
+
110
+ - **`generated.h` not last / missing** — compile errors like "Cannot find generated header" or
111
+ "Expected an include". It must be the final include in the header.
112
+ - **Forgetting `GENERATED_BODY()`** — UHT (Unreal Header Tool) errors; it must be the first
113
+ thing inside the class body.
114
+ - **Raw `UObject*` without `UPROPERTY`** — the garbage collector doesn't see it and may destroy
115
+ it out from under you. Track every UObject pointer with `UPROPERTY` (use `TObjectPtr` in UE5).
116
+ - **Header/UPROPERTY edits with Live Coding** — Live Coding handles function bodies, but
117
+ changes to `UCLASS`/`UPROPERTY`/headers need a full editor restart + rebuild.
118
+ - **Wrong class prefix** — naming an Actor `UFoo` (or a component `AFoo`) breaks UHT; match the
119
+ prefix to the base type.
120
+ - **Unresolved external symbol at link** — the module providing the API isn't in `Build.cs`
121
+ `PublicDependencyModuleNames`.
122
+ - **Not calling `Super::`** in overridden `BeginPlay`/`Tick`/etc. skips engine setup.
123
+
124
+ ## References
125
+
126
+ - For `UActorComponent` creation/attachment, the `UPROPERTY` garbage-collection ownership rules
127
+ (`TObjectPtr`, `TArray<TObjectPtr<>>`, `AddToRoot`), and a replication primer, read
128
+ `references/components-and-gc.md`.
129
+ - Primary docs: "Unreal Engine CPP Quick Start" and "Gameplay Framework"
130
+ (`https://dev.epicgames.com/documentation/en-us/unreal-engine/gameplay-framework-in-unreal-engine`).
131
+
132
+ ## Related skills
133
+
134
+ - `/ue-blueprints` — exposing C++ to designers; BP/C++ interop.
135
+ - `/ue-input-system` — binding input in a C++ Pawn/Character.
136
+ - `/ue-ai-navigation` — C++ AI tasks driven from a behaviour tree.