claude-code-arcane 2.2.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 (144) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +104 -55
  3. package/agents/career/career-strategist.md +88 -0
  4. package/agents/career/cv-reviewer.md +81 -0
  5. package/agents/career/hiring-manager.md +68 -0
  6. package/agents/career/mock-interviewer.md +76 -0
  7. package/agents/freelance/client-screener.md +84 -0
  8. package/agents/freelance/discovery-call.md +65 -0
  9. package/agents/freelance/pipeline-strategist.md +99 -0
  10. package/agents/freelance/proposal-reviewer.md +82 -0
  11. package/agents/game/ue-blueprint-specialist.md +125 -0
  12. package/agents/game/ue-gas-specialist.md +133 -0
  13. package/agents/game/ue-umg-specialist.md +131 -0
  14. package/dist/cli.js +57 -13
  15. package/docs/RELEASE-SETUP.md +1 -1
  16. package/docs/SKILLS-CATALOG.md +67 -4
  17. package/docs/USER-GUIDE.md +77 -72
  18. package/docs/agent-hierarchy.md +21 -1
  19. package/docs/presentations/build_arcane_deck.py +2 -2
  20. package/docs/presentations/build_arcane_deck_12.py +2 -2
  21. package/hooks/detect-unreal.sh +90 -0
  22. package/package.json +2 -2
  23. package/profiles/freelance.yaml +43 -0
  24. package/profiles/job-hunt.yaml +5 -2
  25. package/profiles/unreal-dev.yaml +97 -0
  26. package/rules/drafter-reviewer.md +16 -3
  27. package/rules/freelance-guardrails.md +53 -0
  28. package/rules/gamedev/unreal-code.md +125 -0
  29. package/skills/career-registry/SKILL.md +95 -0
  30. package/skills/career-registry/scripts/career_registry.py +488 -0
  31. package/skills/client-screen/SKILL.md +83 -0
  32. package/skills/client-screen/references/red-flags.md +95 -0
  33. package/skills/cover-letter/SKILL.md +4 -3
  34. package/skills/cv-tailor/SKILL.md +11 -2
  35. package/skills/freelance-deliver/SKILL.md +90 -0
  36. package/skills/freelance-hunt/SKILL.md +126 -0
  37. package/skills/freelance-hunt/references/templates/Connects.md +39 -0
  38. package/skills/freelance-hunt/references/templates/Contrato.md +60 -0
  39. package/skills/freelance-hunt/references/templates/Propuesta.md +66 -0
  40. package/skills/freelance-kickoff/SKILL.md +94 -0
  41. package/skills/freelance-pipeline/SKILL.md +109 -0
  42. package/skills/freelance-pipeline/scripts/import_history.py +425 -0
  43. package/skills/freelance-profile/SKILL.md +93 -0
  44. package/skills/freelance-proposal/SKILL.md +112 -0
  45. package/skills/freelance-scan/SKILL.md +196 -0
  46. package/skills/freelance-scan/references/dedup.md +35 -0
  47. package/skills/freelance-scan/references/platforms.md +167 -0
  48. package/skills/freelance-scan/scripts/freelance_search.py +1077 -0
  49. package/skills/install-mcp/SKILL.md +135 -46
  50. package/skills/install-mcp/references/unreal-operations.md +55 -0
  51. package/skills/interview-prep/SKILL.md +11 -3
  52. package/skills/job-aplicar/SKILL.md +5 -1
  53. package/skills/job-hunt/SKILL.md +17 -0
  54. package/skills/job-outcome/SKILL.md +15 -1
  55. package/skills/job-upskill/SKILL.md +10 -2
  56. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  57. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  58. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  59. package/skills/ue-ai-navigation/SKILL.md +526 -0
  60. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  61. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  62. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  63. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  64. package/skills/ue-animation-system/SKILL.md +521 -0
  65. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  66. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  67. package/skills/ue-async-threading/SKILL.md +433 -0
  68. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  69. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  70. package/skills/ue-audio-system/SKILL.md +527 -0
  71. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  72. package/skills/ue-blueprints/SKILL.md +134 -0
  73. package/skills/ue-blueprints/references/communication.md +82 -0
  74. package/skills/ue-character-movement/SKILL.md +475 -0
  75. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  76. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  77. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  78. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  79. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  80. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  81. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  82. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  83. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  84. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  85. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  86. package/skills/ue-editor-tools/SKILL.md +523 -0
  87. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  88. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  89. package/skills/ue-game-features/SKILL.md +477 -0
  90. package/skills/ue-game-features/references/experience-system.md +222 -0
  91. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  92. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  93. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  94. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  95. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  96. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  97. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  98. package/skills/ue-input-system/SKILL.md +394 -0
  99. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  100. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  101. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  102. package/skills/ue-mass-entity/SKILL.md +476 -0
  103. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  104. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  105. package/skills/ue-materials-rendering/SKILL.md +526 -0
  106. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  107. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  108. package/skills/ue-module-build-system/SKILL.md +487 -0
  109. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  110. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  111. package/skills/ue-naming-conventions/SKILL.md +142 -0
  112. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  113. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  114. package/skills/ue-networking-replication/SKILL.md +452 -0
  115. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  116. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  117. package/skills/ue-niagara-effects/SKILL.md +531 -0
  118. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  119. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  120. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  121. package/skills/ue-packaging/SKILL.md +116 -0
  122. package/skills/ue-physics-collision/SKILL.md +423 -0
  123. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  124. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  125. package/skills/ue-procedural-generation/SKILL.md +505 -0
  126. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  127. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  128. package/skills/ue-project-context/SKILL.md +305 -0
  129. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  130. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  131. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  132. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  133. package/skills/ue-state-trees/SKILL.md +489 -0
  134. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  135. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  136. package/skills/ue-testing-debugging/SKILL.md +520 -0
  137. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  138. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  139. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  140. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  141. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  142. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  143. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  144. package/templates/gamedev/unreal-project-context.md +107 -0
@@ -0,0 +1,531 @@
1
+ ---
2
+ name: ue-niagara-effects
3
+ description: "Niagara particle systems, VFX, emitters, data interfaces, GPU simulation"
4
+ category: "gamedev"
5
+ argument-hint: "[effect o system]"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit
8
+ ---
9
+
10
+ # UE Niagara Effects
11
+
12
+ You are an expert in controlling Unreal Engine's Niagara VFX system from C++.
13
+
14
+ Ask before writing: this skill edits Niagara system and emitter assets. Propose the change and get approval before using Write or Edit.
15
+
16
+ ## Context Check
17
+
18
+ Read `docs/unreal/project-context.md` before proceeding. Confirm:
19
+
20
+ - The `Niagara` plugin is listed under enabled plugins (`Plugins/FX/Niagara`).
21
+ - The target module's `Build.cs` has `"Niagara"` (and optionally `"NiagaraCore"`) in `PublicDependencyModuleNames`.
22
+ - Platform targets: note whether mobile or dedicated-server builds are in scope, because Niagara is
23
+ typically suppressed on dedicated servers and may need LOD simplification on mobile.
24
+
25
+ ## Information Gathering
26
+
27
+ Before writing Niagara C++ code, clarify:
28
+
29
+ 1. **Effect lifecycle** — one-shot (fire and forget) or persistent / looping?
30
+ 2. **Parameter needs** — which Niagara User Parameters must be set from gameplay (positions, colors, scalars)?
31
+ 3. **Data interfaces required** — SkeletalMesh, StaticMesh, Curve, Array, or custom?
32
+ 4. **Simulation target** — CPU or GPU sim? (affects which DI features are available)
33
+ 5. **Performance budget** — pooling required? Mobile scalability tier?
34
+ 6. **Completion handling** — does gameplay need a callback when the effect finishes?
35
+
36
+ ---
37
+
38
+ ## System Structure (UE Concept Map)
39
+
40
+ ```
41
+ UNiagaraSystem (asset: UNiagaraSystem)
42
+ └── UNiagaraEmitter[] (per-emitter asset, referenced via FNiagaraEmitterHandle)
43
+ └── UNiagaraScript[] (Spawn / Update / Event scripts; authored in Niagara editor)
44
+ └── Modules (stack of NiagaraScript nodes; not C++ classes)
45
+
46
+ Runtime instances:
47
+ UNiagaraComponent (scene component that drives one UNiagaraSystem instance)
48
+ └── FNiagaraSystemInstance (internal runtime state; access via GetSystemInstanceController())
49
+ ```
50
+
51
+ **Key rule**: authors expose parameters to C++ by setting their namespace to `User.` in the Niagara
52
+ editor. Only `User.*` parameters can be overridden at runtime from C++.
53
+
54
+ ---
55
+
56
+ ## Spawning Niagara Systems
57
+
58
+ ### Fire-and-Forget (One-Shot) at World Location
59
+
60
+ ```cpp
61
+ #include "NiagaraFunctionLibrary.h"
62
+ #include "NiagaraComponent.h"
63
+
64
+ // Minimal one-shot spawn — component auto-destroys when the system completes.
65
+ UNiagaraComponent* NiagaraComp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
66
+ this, // WorldContextObject
67
+ ImpactVFXSystem, // UPROPERTY(EditAnywhere) UNiagaraSystem*
68
+ HitLocation, // FVector Location
69
+ FRotator::ZeroRotator, // FRotator Rotation
70
+ FVector(1.f), // FVector Scale
71
+ /*bAutoDestroy=*/ true,
72
+ /*bAutoActivate=*/ true,
73
+ /*PoolingMethod=*/ ENCPoolMethod::AutoRelease, // use pool when available
74
+ /*bPreCullCheck=*/ true
75
+ );
76
+
77
+ // Set parameters before the first tick if needed.
78
+ if (NiagaraComp)
79
+ {
80
+ NiagaraComp->SetVariableVec3(FName("User.HitNormal"), HitNormal);
81
+ NiagaraComp->SetVariableLinearColor(FName("User.HitColor"), DamageColor);
82
+ }
83
+ ```
84
+
85
+ ### Attached to a Component (Persistent / Looping)
86
+
87
+ ```cpp
88
+ // Attaches to a socket and stays active until manually deactivated.
89
+ UNiagaraComponent* TrailComp = UNiagaraFunctionLibrary::SpawnSystemAttached(
90
+ TrailVFXSystem,
91
+ WeaponMesh, // USceneComponent* AttachToComponent
92
+ FName("MuzzleSocket"), // FName AttachPointName
93
+ FVector::ZeroVector,
94
+ FRotator::ZeroRotator,
95
+ EAttachLocation::SnapToTarget,
96
+ /*bAutoDestroy=*/ false,
97
+ /*bAutoActivate=*/ true,
98
+ ENCPoolMethod::ManualRelease,
99
+ /*bPreCullCheck=*/ true
100
+ );
101
+ ```
102
+
103
+ ### Persistent Component on an Actor (Preferred for Repeated Use)
104
+
105
+ ```cpp
106
+ // In header:
107
+ UPROPERTY(VisibleAnywhere)
108
+ TObjectPtr<UNiagaraComponent> EngineTrailVFX;
109
+
110
+ // In constructor:
111
+ EngineTrailVFX = CreateDefaultSubobject<UNiagaraComponent>(TEXT("EngineTrailVFX"));
112
+ EngineTrailVFX->SetupAttachment(GetRootComponent());
113
+ EngineTrailVFX->SetAutoActivate(false); // start inactive; activate via gameplay
114
+
115
+ // In gameplay code:
116
+ EngineTrailVFX->SetAsset(EngineTrailSystem); // swap asset without destroying component
117
+ EngineTrailVFX->Activate(/*bReset=*/ true);
118
+ ```
119
+
120
+ ### Lifecycle Control
121
+
122
+ ```cpp
123
+ NiagaraComp->Activate(/*bReset=*/ false); // activate; resume if paused
124
+ NiagaraComp->Activate(/*bReset=*/ true); // activate with full reset
125
+ NiagaraComp->Deactivate(); // stop spawning, let particles drain
126
+ NiagaraComp->DeactivateImmediate(); // kill all particles immediately
127
+ NiagaraComp->ResetSystem(); // restart from time 0
128
+ NiagaraComp->ReinitializeSystem(); // full re-init + restart (expensive; prefer ResetSystem)
129
+ NiagaraComp->SetPaused(true); // pause simulation
130
+ NiagaraComp->SetAutoDestroy(true); // destroy component when system finishes
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Setting Parameters from C++
136
+
137
+ All setter variants accept the parameter name as `FName` prefixed with its namespace.
138
+ User-exposed parameters use the `User.` prefix.
139
+
140
+ ```cpp
141
+ // Scalar types
142
+ NiagaraComp->SetVariableFloat(FName("User.DamageAmount"), 150.f);
143
+ NiagaraComp->SetVariableInt(FName("User.ProjectileCount"), 12);
144
+ NiagaraComp->SetVariableBool(FName("User.bIsCritical"), bIsCriticalHit);
145
+
146
+ // Vector types
147
+ NiagaraComp->SetVariableVec2(FName("User.UVOffset"), FVector2D(0.5, 0.25));
148
+ NiagaraComp->SetVariableVec3(FName("User.TargetPosition"), TargetLocation);
149
+ NiagaraComp->SetVariableVec4(FName("User.CustomData"), FVector4(1, 0.5, 0, 1));
150
+ NiagaraComp->SetVariableLinearColor(FName("User.TintColor"), FLinearColor::Red);
151
+ NiagaraComp->SetVariableQuat(FName("User.Orientation"), GetActorQuat());
152
+
153
+ // Object / Actor references (binds DI override)
154
+ NiagaraComp->SetVariableObject(FName("User.TargetMesh"), SkeletalMeshComponent);
155
+ NiagaraComp->SetVariableActor(FName("User.SourceActor"), this);
156
+
157
+ // Position (LWC-aware alias for vec3)
158
+ NiagaraComp->SetVariablePosition(FName("User.WorldOrigin"), WorldSpaceOrigin);
159
+
160
+ // Material / Texture overrides
161
+ NiagaraComp->SetVariableMaterial(FName("User.FXMaterial"), DynamicMaterial);
162
+ NiagaraComp->SetVariableTexture(FName("User.FlowMap"), FlowTexture);
163
+
164
+ // Read a float parameter back (returns bIsValid=false when name not found)
165
+ bool bIsValid = false;
166
+ float CurrentValue = NiagaraComp->GetVariableFloat(FName("User.EmitRate"), bIsValid);
167
+ ```
168
+
169
+ ### Blueprint-Accessible Legacy Signatures (prefer FName variants above)
170
+
171
+ ```cpp
172
+ // Old FString signatures still work but are slower due to FName conversion.
173
+ NiagaraComp->SetNiagaraVariableFloat(TEXT("User.SpeedScale"), 2.f);
174
+ NiagaraComp->SetNiagaraVariableVec3(TEXT("User.ImpactPoint"), Location);
175
+ NiagaraComp->SetNiagaraVariableLinearColor(TEXT("User.Color"), FLinearColor::Blue);
176
+ ```
177
+
178
+ ### Parameter Namespaces Reference
179
+
180
+ | Namespace prefix | Settable from C++ | Description |
181
+ |------------------|-------------------|--------------------------------------|
182
+ | `User.` | Yes | User-exposed; main runtime override |
183
+ | `System.` | No (read-only) | System-level built-ins (Age, DeltaTime, etc.) |
184
+ | `Emitter.` | No (internal) | Per-emitter variables |
185
+ | `Particle.` | No (internal) | Per-particle variables |
186
+
187
+ See `references/niagara-parameter-types.md` for the full C++ type to Niagara type mapping.
188
+
189
+ ---
190
+
191
+ ## Data Interfaces from C++
192
+
193
+ Data interfaces (DIs) are `UObject`-derived assets that expose structured external data to Niagara
194
+ scripts. They appear as `User.*` parameters of DI type in the Niagara editor, and are overridden
195
+ at runtime via `SetVariableObject` or the specialized function library helpers.
196
+
197
+ ### Binding Skeletal Mesh DI
198
+
199
+ ```cpp
200
+ #include "NiagaraFunctionLibrary.h"
201
+
202
+ // Override the "User.SourceMesh" skeletal mesh DI on a running component.
203
+ UNiagaraFunctionLibrary::OverrideSystemUserVariableSkeletalMeshComponent(
204
+ NiagaraComp,
205
+ TEXT("User.SourceMesh"), // must match the DI's User parameter name in the asset
206
+ GetMesh() // USkeletalMeshComponent*
207
+ );
208
+
209
+ // Restrict which bones spawn from (destructive — modifies the DI instance data).
210
+ UNiagaraFunctionLibrary::SetSkeletalMeshDataInterfaceFilteredBones(
211
+ NiagaraComp,
212
+ TEXT("User.SourceMesh"),
213
+ { FName("hand_l"), FName("hand_r") }
214
+ );
215
+
216
+ // Restrict which sampling regions to use.
217
+ UNiagaraFunctionLibrary::SetSkeletalMeshDataInterfaceSamplingRegions(
218
+ NiagaraComp,
219
+ TEXT("User.SourceMesh"),
220
+ { FName("HeadRegion") }
221
+ );
222
+ ```
223
+
224
+ ### Binding Static Mesh DI
225
+
226
+ ```cpp
227
+ // Override via component reference.
228
+ UNiagaraFunctionLibrary::OverrideSystemUserVariableStaticMeshComponent(
229
+ NiagaraComp,
230
+ TEXT("User.ScatterMesh"),
231
+ StaticMeshComp
232
+ );
233
+
234
+ // Override with a raw UStaticMesh asset pointer.
235
+ UNiagaraFunctionLibrary::OverrideSystemUserVariableStaticMesh(
236
+ NiagaraComp,
237
+ TEXT("User.ScatterMesh"),
238
+ LoadedStaticMesh
239
+ );
240
+ ```
241
+
242
+ ### Reading / Modifying an Array DI at Runtime
243
+
244
+ ```cpp
245
+ #include "NiagaraDataInterfaceArrayFunctionLibrary.h"
246
+
247
+ // Push a new float array into the effect (e.g., damage heatmap values).
248
+ TArray<float> HeatValues = ComputeHeatValues();
249
+ UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayFloat(
250
+ NiagaraComp, FName("User.HeatData"), HeatValues
251
+ );
252
+
253
+ // Update a single element without replacing the whole array.
254
+ UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayFloatValue(
255
+ NiagaraComp, FName("User.HeatData"), /*Index=*/ 5, /*Value=*/ 0.9f, /*bSizeToFit=*/ false
256
+ );
257
+
258
+ // Other strongly-typed array setters available:
259
+ // SetNiagaraArrayVector, SetNiagaraArrayVector4, SetNiagaraArrayColor,
260
+ // SetNiagaraArrayQuat, SetNiagaraArrayInt32, SetNiagaraArrayBool, etc.
261
+ ```
262
+
263
+ ### Direct DI Object Access (Advanced)
264
+
265
+ ```cpp
266
+ // Retrieve the actual DI UObject to mutate its properties directly.
267
+ // Template variant resolves the cast automatically.
268
+ UNiagaraDataInterfaceCurve* CurveDI =
269
+ UNiagaraFunctionLibrary::GetDataInterface<UNiagaraDataInterfaceCurve>(
270
+ NiagaraComp, FName("User.SpeedCurve")
271
+ );
272
+
273
+ if (CurveDI)
274
+ {
275
+ // Mutate curve keyframes at runtime (rebuilds LUT internally).
276
+ CurveDI->Curve.AddKey(0.f, 0.f);
277
+ CurveDI->Curve.AddKey(1.f, 500.f);
278
+ // UpdateLUT() is WITH_EDITORONLY_DATA — only call in editor builds.
279
+ #if WITH_EDITORONLY_DATA
280
+ CurveDI->UpdateLUT();
281
+ #endif
282
+ }
283
+
284
+ // Non-template variant when the DI class is only known at runtime.
285
+ UNiagaraDataInterface* GenericDI =
286
+ UNiagaraFunctionLibrary::GetDataInterface(
287
+ UNiagaraDataInterfaceStaticMesh::StaticClass(),
288
+ NiagaraComp,
289
+ FName("User.ImpactMesh")
290
+ );
291
+ ```
292
+
293
+ See `references/niagara-data-interfaces.md` for the full built-in DI catalogue.
294
+
295
+ **Custom Data Interfaces**: Subclass `UNiagaraDataInterface`, override `GetFunctions()` to define
296
+ available functions, `GetVMExternalFunction()` to bind C++ implementations, and optionally
297
+ `ProvidePerInstanceDataForRenderThread()` for GPU access. Register in the module's `StartupModule`.
298
+ This enables game-specific data (inventory, terrain) to feed directly into Niagara systems.
299
+
300
+ ---
301
+
302
+ ## Completion Callbacks
303
+
304
+ ```cpp
305
+ // Bind a C++ delegate to fire when the Niagara system finishes all particles.
306
+ // FOnNiagaraSystemFinished is DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(, UNiagaraComponent*)
307
+ NiagaraComp->OnSystemFinished.AddDynamic(this, &UMyComponent::OnVFXFinished);
308
+
309
+ // The callback signature:
310
+ UFUNCTION()
311
+ void UMyComponent::OnVFXFinished(UNiagaraComponent* FinishedComponent)
312
+ {
313
+ // Called on game thread when every particle has expired and the system is done.
314
+ FinishedComponent->DestroyComponent();
315
+ // or return it to pool, notify gameplay, etc.
316
+ }
317
+
318
+ // Unbind when the owner is destroyed to avoid stale delegates.
319
+ NiagaraComp->OnSystemFinished.RemoveDynamic(this, &UMyComponent::OnVFXFinished);
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Performance: Pooling
325
+
326
+ `ENCPoolMethod` controls the pool behavior on every spawn call:
327
+
328
+ - `AutoRelease` — component returns to the world pool automatically when the system finishes.
329
+ Pass `bAutoDestroy=true`; the pool handles actual reclaim.
330
+ - `ManualRelease` — you control when the component returns; call `ReleaseToPool()` to reclaim.
331
+ - `None` — no pooling; component is destroyed when finished if `bAutoDestroy=true`.
332
+
333
+ ```cpp
334
+ // AutoRelease: most common for one-shots (explosions, impacts).
335
+ UNiagaraComponent* Comp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
336
+ this, ExplosionSystem, Location, FRotator::ZeroRotator,
337
+ FVector(1.f), /*bAutoDestroy=*/true, /*bAutoActivate=*/true,
338
+ ENCPoolMethod::AutoRelease
339
+ );
340
+
341
+ // ManualRelease: for effects you pause/resume (e.g., a beam while a button is held).
342
+ // Reclaim by calling ReleaseToPool() when done.
343
+ TrailComp->ReleaseToPool();
344
+
345
+ // Prime the pool before a gameplay-critical moment via FNiagaraWorldManager.
346
+ if (FNiagaraWorldManager* NiagaraWorldMan = FNiagaraWorldManager::Get(GetWorld()))
347
+ {
348
+ NiagaraWorldMan->GetComponentPool()->PrimePool(ExplosionSystem, GetWorld());
349
+ }
350
+ ```
351
+
352
+ Pool capacity is configured per-system in the `UNiagaraSystem` pooling settings (not a global CVar).
353
+ Relevant global pool CVars: `FX.NiagaraComponentPool.Enable` (1/0) and
354
+ `FX.NiagaraComponentPool.KillUnusedTime` (seconds before idle components are culled).
355
+
356
+ ---
357
+
358
+ ## Performance: Scalability and LOD
359
+
360
+ ```cpp
361
+ // Allow the scalability manager to cull this component based on distance and budget.
362
+ NiagaraComp->SetAllowScalability(true); // default true; disable for gameplay-critical VFX
363
+
364
+ // Adjust tick behavior to avoid unnecessary dependency resolution.
365
+ // ENiagaraTickBehavior::UsePrereqs — default; ticks after its prerequisites
366
+ // ENiagaraTickBehavior::ForceTickFirst — useful for VFX that leads all tick groups
367
+ NiagaraComp->SetTickBehavior(ENiagaraTickBehavior::UsePrereqs);
368
+ ```
369
+
370
+ Scalability per platform is configured in the `UNiagaraEffectType` asset assigned to the
371
+ `UNiagaraSystem`. The effect type defines quality tiers (Low / Medium / High / Epic) and which
372
+ emitters are stripped at each tier. This is data-driven; no C++ changes needed per platform.
373
+
374
+ **GPU vs CPU simulation trade-offs**:
375
+ - CPU sim: particle data is readable/writable from C++ each frame; lower particle counts; supports
376
+ all DI types.
377
+ - GPU sim: supports hundreds of thousands of particles; DI support is limited (not all CPU-side DIs
378
+ have GPU equivalents); particle data is not readable back to CPU without readbacks.
379
+
380
+ **Determinism**: GPU simulations are inherently non-deterministic. For multiplayer VFX that must
381
+ match across clients, use CPU simulation with `FixedTickDelta` on the emitter. Cosmetic-only
382
+ effects should spawn client-side only — skip them on dedicated servers entirely.
383
+
384
+ ---
385
+
386
+ ## Warm-Up, Server Handling, and Events
387
+
388
+ **Pre-simulation (warm-up)**: seek to a desired age before the effect is visible.
389
+ ```cpp
390
+ NiagaraComp->SetDesiredAge(2.5f); // age in seconds
391
+ NiagaraComp->SeekToDesiredAge(2.5f); // perform seek immediately (skips simulation steps)
392
+ // FFXSystemSpawnParameters (used by SpawnSystemAtLocationWithParams) also exposes DesiredAge.
393
+ ```
394
+
395
+ **Dedicated server**: `SpawnSystemAtLocation` returns `nullptr` on dedicated servers. Always null-check
396
+ the returned component and guard VFX spawns with `!IsRunningDedicatedServer()` where needed.
397
+
398
+ **Gameplay events to Niagara**: Niagara's internal event system (Location Events, Death Events,
399
+ Collision Events) is configured in the Niagara editor between emitters. From C++, trigger a
400
+ gameplay-driven burst by updating a User bool parameter that the spawn script reads:
401
+ ```cpp
402
+ NiagaraComp->SetVariableBool(FName("User.bJustDied"), true);
403
+ // Niagara reads this flag on the next spawn script tick and fires the burst.
404
+ // There is no C++ API to inject raw Niagara events directly — use User parameters as the bridge.
405
+ ```
406
+
407
+ ---
408
+
409
+ ## Required Build.cs
410
+
411
+ ```csharp
412
+ PublicDependencyModuleNames.AddRange(new string[]
413
+ {
414
+ "Core",
415
+ "CoreUObject",
416
+ "Engine",
417
+ "Niagara", // UNiagaraComponent, UNiagaraFunctionLibrary, UNiagaraSystem
418
+ "NiagaraCore", // UNiagaraDataInterface base (NiagaraCore module)
419
+ });
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Common Mistakes and Anti-Patterns
425
+
426
+ **Spawning a new system component every tick**
427
+ ```cpp
428
+ // BAD: Creates a new UNiagaraComponent each frame. Destroys performance.
429
+ void AMyActor::Tick(float DeltaTime)
430
+ {
431
+ UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, TrailFX, GetActorLocation());
432
+ }
433
+
434
+ // GOOD: Create the component once in BeginPlay or constructor; activate/deactivate as needed.
435
+ ```
436
+
437
+ **Wrong parameter namespace**
438
+ ```cpp
439
+ // BAD: "Emitter.Speed" is an internal emitter parameter; cannot be set from C++.
440
+ NiagaraComp->SetVariableFloat(FName("Emitter.Speed"), 300.f);
441
+
442
+ // GOOD: The Niagara author must expose the parameter under "User.*".
443
+ NiagaraComp->SetVariableFloat(FName("User.Speed"), 300.f);
444
+ ```
445
+
446
+ **Type mismatch between C++ and Niagara**
447
+ ```cpp
448
+ // BAD: Calling SetVariableVec3 on a parameter that is typed as "Color" in Niagara.
449
+ // Silently fails — no runtime error, parameter is just not updated.
450
+ NiagaraComp->SetVariableVec3(FName("User.TintColor"), FVector(1, 0, 0));
451
+
452
+ // GOOD: Match the C++ call to the Niagara parameter type.
453
+ NiagaraComp->SetVariableLinearColor(FName("User.TintColor"), FLinearColor::Red);
454
+ ```
455
+
456
+ **Setting parameters after system completes**
457
+ ```cpp
458
+ // The component is valid but the system instance may be inactive. Check before setting.
459
+ if (NiagaraComp && NiagaraComp->IsActive())
460
+ {
461
+ NiagaraComp->SetVariableFloat(FName("User.Intensity"), NewIntensity);
462
+ }
463
+ ```
464
+
465
+ **Forgetting to check nullptr on spawn (especially on server)**
466
+ ```cpp
467
+ UNiagaraComponent* Comp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(...);
468
+ // Comp can be nullptr on dedicated server or when bPreCullCheck rejects the spawn.
469
+ if (Comp)
470
+ {
471
+ Comp->SetVariableFloat(FName("User.Scale"), 2.f);
472
+ }
473
+ ```
474
+
475
+ **Not removing delegates before destruction**
476
+ ```cpp
477
+ // BAD: OnSystemFinished fires after owner is garbage collected → crash.
478
+ // GOOD: Always RemoveDynamic in BeginDestroy or EndPlay.
479
+ void AMyActor::EndPlay(const EEndPlayReason::Type Reason)
480
+ {
481
+ if (NiagaraComp)
482
+ {
483
+ NiagaraComp->OnSystemFinished.RemoveDynamic(this, &AMyActor::OnVFXFinished);
484
+ }
485
+ Super::EndPlay(Reason);
486
+ }
487
+ ```
488
+
489
+ **Niagara Fluids (experimental)**: The Niagara Fluids plugin provides GPU-based fluid and gas
490
+ simulations. It is experimental, GPU-only, and carries a high performance cost — profile carefully
491
+ before shipping and restrict use to hero effects where visual impact justifies the budget.
492
+
493
+ **World space vs local space**: Use local-space simulation for effects attached to moving actors
494
+ (particles inherit the parent component's transform and move with it). Use world-space simulation
495
+ for effects that should remain stationary after emission (e.g., a ground impact crater where
496
+ particles should not follow a moving actor). The space setting lives on each emitter in the Niagara editor.
497
+
498
+ ---
499
+
500
+ ## Related Skills
501
+
502
+ - `/ue-actor-component-architecture` — component creation, attachment, lifecycle
503
+ - `/ue-materials-rendering` — particle material setup, dynamic material instances
504
+ - `/ue-cpp-foundations` — UObject lifetime, delegates, UPROPERTY references
505
+ - `/ue-naming-conventions` — asset prefixes and Content folder layout for these assets
506
+
507
+ ## Reference Files
508
+
509
+ - `references/niagara-parameter-types.md` — C++ type to Niagara type mapping
510
+ - `references/niagara-data-interfaces.md` — built-in data interface catalogue
511
+ - `references/niagara-workflow.md` — editor-side authoring flow: systems, emitters, modules, parameters
512
+
513
+ ---
514
+
515
+ ## Checklist
516
+
517
+ Validate before considering the work done:
518
+
519
+ - [ ] Particle counts and spawn rates are bounded, with scalability settings set
520
+ - [ ] GPU simulation is used where the effect is heavy and does not need CPU readback
521
+ - [ ] Effects are pooled or auto-destroyed rather than leaking components
522
+ - [ ] Conventions in `docs/unreal/project-context.md` were read and followed
523
+ - [ ] The Common Mistakes section of this skill was reviewed against the change
524
+
525
+ **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.
526
+
527
+ ## Next Steps
528
+
529
+ - `/perf-profile` — confirm the VFX budget holds under load
530
+ - `/code-review` — architectural and quality review of the change
531
+ - `/commit` — conventional commit once the change is verified