[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6FERDKF2DNZPYKQHH2H8HE0":3,"skill-same:01M6FERDKF2DNZPYKQHH2H8HE0":46},{"skillNo":4,"name":5,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":21,"frontMatter":22,"refName":23,"commitSha":24,"skillDirectory":25,"forksCount":26,"repositoryHtmlUrl":27,"repositoryHomepageUrl":28,"ownerHtmlUrl":29,"ownerBlogUrl":6,"ownerDescription":30,"updatedAt":31,"install":32,"resources":45},"01M6FERDKF2DNZPYKQHH2H8HE0","ue-niagara-effects",null,"ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",66,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fquodsoler\u002Funreal-engine-skills\u002Fue-niagara-effects\u002FSKILL.md",[],[18],"ue niagara effects","2026-11-10 10:12:30",false,"# UE Niagara Effects\n\nYou are an expert in controlling Unreal Engine's Niagara VFX system from C++.\n\n## Context Check\n\nRead `.agents\u002Fue-project-context.md` before proceeding. Confirm:\n\n- The `Niagara` plugin is listed under enabled plugins (`Plugins\u002FFX\u002FNiagara`).\n- The target module's `Build.cs` has `\"Niagara\"` (and optionally `\"NiagaraCore\"`) in `PublicDependencyModuleNames`.\n- Platform targets: note whether mobile or dedicated-server builds are in scope, because Niagara is\n  typically suppressed on dedicated servers and may need LOD simplification on mobile.\n\n## Information Gathering\n\nBefore writing Niagara C++ code, clarify:\n\n1. **Effect lifecycle** — one-shot (fire and forget) or persistent \u002F looping?\n2. **Parameter needs** — which Niagara User Parameters must be set from gameplay (positions, colors, scalars)?\n3. **Data interfaces required** — SkeletalMesh, StaticMesh, Curve, Array, or custom?\n4. **Simulation target** — CPU or GPU sim? (affects which DI features are available)\n5. **Performance budget** — pooling required? Mobile scalability tier?\n6. **Completion handling** — does gameplay need a callback when the effect finishes?\n\n---\n\n## System Structure (UE Concept Map)\n\n```\nUNiagaraSystem  (asset: UNiagaraSystem)\n  └── UNiagaraEmitter[]         (per-emitter asset, referenced via FNiagaraEmitterHandle)\n        └── UNiagaraScript[]   (Spawn \u002F Update \u002F Event scripts; authored in Niagara editor)\n              └── Modules       (stack of NiagaraScript nodes; not C++ classes)\n\nRuntime instances:\n  UNiagaraComponent             (scene component that drives one UNiagaraSystem instance)\n    └── FNiagaraSystemInstance  (internal runtime state; access via GetSystemInstanceController())\n```\n\n**Key rule**: authors expose parameters to C++ by setting their namespace to `User.` in the Niagara\neditor. Only `User.*` parameters can be overridden at runtime from C++.\n\n---\n\n## Spawning Niagara Systems\n\n### Fire-and-Forget (One-Shot) at World Location\n\n```cpp\n#include \"NiagaraFunctionLibrary.h\"\n#include \"NiagaraComponent.h\"\n\n\u002F\u002F Minimal one-shot spawn — component auto-destroys when the system completes.\nUNiagaraComponent* NiagaraComp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(\n    this,                           \u002F\u002F WorldContextObject\n    ImpactVFXSystem,                \u002F\u002F UPROPERTY(EditAnywhere) UNiagaraSystem*\n    HitLocation,                    \u002F\u002F FVector Location\n    FRotator::ZeroRotator,          \u002F\u002F FRotator Rotation\n    FVector(1.f),                   \u002F\u002F FVector Scale\n    \u002F*bAutoDestroy=*\u002F true,\n    \u002F*bAutoActivate=*\u002F true,\n    \u002F*PoolingMethod=*\u002F ENCPoolMethod::AutoRelease,   \u002F\u002F use pool when available\n    \u002F*bPreCullCheck=*\u002F true\n);\n\n\u002F\u002F Set parameters before the first tick if needed.\nif (NiagaraComp)\n{\n    NiagaraComp->SetVariableVec3(FName(\"User.HitNormal\"), HitNormal);\n    NiagaraComp->SetVariableLinearColor(FName(\"User.HitColor\"), DamageColor);\n}\n```\n\n### Attached to a Component (Persistent \u002F Looping)\n\n```cpp\n\u002F\u002F Attaches to a socket and stays active until manually deactivated.\nUNiagaraComponent* TrailComp = UNiagaraFunctionLibrary::SpawnSystemAttached(\n    TrailVFXSystem,\n    WeaponMesh,                             \u002F\u002F USceneComponent* AttachToComponent\n    FName(\"MuzzleSocket\"),                  \u002F\u002F FName AttachPointName\n    FVector::ZeroVector,\n    FRotator::ZeroRotator,\n    EAttachLocation::SnapToTarget,\n    \u002F*bAutoDestroy=*\u002F false,\n    \u002F*bAutoActivate=*\u002F true,\n    ENCPoolMethod::ManualRelease,\n    \u002F*bPreCullCheck=*\u002F true\n);\n```\n\n### Persistent Component on an Actor (Preferred for Repeated Use)\n\n```cpp\n\u002F\u002F In header:\nUPROPERTY(VisibleAnywhere)\nTObjectPtr\u003CUNiagaraComponent> EngineTrailVFX;\n\n\u002F\u002F In constructor:\nEngineTrailVFX = CreateDefaultSubobject\u003CUNiagaraComponent>(TEXT(\"EngineTrailVFX\"));\nEngineTrailVFX->SetupAttachment(GetRootComponent());\nEngineTrailVFX->SetAutoActivate(false);   \u002F\u002F start inactive; activate via gameplay\n\n\u002F\u002F In gameplay code:\nEngineTrailVFX->SetAsset(EngineTrailSystem);    \u002F\u002F swap asset without destroying component\nEngineTrailVFX->Activate(\u002F*bReset=*\u002F true);\n```\n\n### Lifecycle Control\n\n```cpp\nNiagaraComp->Activate(\u002F*bReset=*\u002F false);       \u002F\u002F activate; resume if paused\nNiagaraComp->Activate(\u002F*bReset=*\u002F true);        \u002F\u002F activate with full reset\nNiagaraComp->Deactivate();                      \u002F\u002F stop spawning, let particles drain\nNiagaraComp->DeactivateImmediate();             \u002F\u002F kill all particles immediately\nNiagaraComp->ResetSystem();                     \u002F\u002F restart from time 0\nNiagaraComp->ReinitializeSystem();              \u002F\u002F full re-init + restart (expensive; prefer ResetSystem)\nNiagaraComp->SetPaused(true);                   \u002F\u002F pause simulation\nNiagaraComp->SetAutoDestroy(true);              \u002F\u002F destroy component when system finishes\n```\n\n---\n\n## Setting Parameters from C++\n\nAll setter variants accept the parameter name as `FName` prefixed with its namespace.\nUser-exposed parameters use the `User.` prefix.\n\n```cpp\n\u002F\u002F Scalar types\nNiagaraComp->SetVariableFloat(FName(\"User.DamageAmount\"), 150.f);\nNiagaraComp->SetVariableInt(FName(\"User.ProjectileCount\"), 12);\nNiagaraComp->SetVariableBool(FName(\"User.bIsCritical\"), bIsCriticalHit);\n\n\u002F\u002F Vector types\nNiagaraComp->SetVariableVec2(FName(\"User.UVOffset\"), FVector2D(0.5, 0.25));\nNiagaraComp->SetVariableVec3(FName(\"User.TargetPosition\"), TargetLocation);\nNiagaraComp->SetVariableVec4(FName(\"User.CustomData\"), FVector4(1, 0.5, 0, 1));\nNiagaraComp->SetVariableLinearColor(FName(\"User.TintColor\"), FLinearColor::Red);\nNiagaraComp->SetVariableQuat(FName(\"User.Orientation\"), GetActorQuat());\n\n\u002F\u002F Object \u002F Actor references (binds DI override)\nNiagaraComp->SetVariableObject(FName(\"User.TargetMesh\"), SkeletalMeshComponent);\nNiagaraComp->SetVariableActor(FName(\"User.SourceActor\"), this);\n\n\u002F\u002F Position (LWC-aware alias for vec3)\nNiagaraComp->SetVariablePosition(FName(\"User.WorldOrigin\"), WorldSpaceOrigin);\n\n\u002F\u002F Material \u002F Texture overrides\nNiagaraComp->SetVariableMaterial(FName(\"User.FXMaterial\"), DynamicMaterial);\nNiagaraComp->SetVariableTexture(FName(\"User.FlowMap\"), FlowTexture);\n\n\u002F\u002F Read a float parameter back (returns bIsValid=false when name not found)\nbool bIsValid = false;\nfloat CurrentValue = NiagaraComp->GetVariableFloat(FName(\"User.EmitRate\"), bIsValid);\n```\n\n### Blueprint-Accessible Legacy Signatures (prefer FName variants above)\n\n```cpp\n\u002F\u002F Old FString signatures still work but are slower due to FName conversion.\nNiagaraComp->SetNiagaraVariableFloat(TEXT(\"User.SpeedScale\"), 2.f);\nNiagaraComp->SetNiagaraVariableVec3(TEXT(\"User.ImpactPoint\"), Location);\nNiagaraComp->SetNiagaraVariableLinearColor(TEXT(\"User.Color\"), FLinearColor::Blue);\n```\n\n### Parameter Namespaces Reference\n\n| Namespace prefix | Settable from C++ | Description                          |\n|------------------|-------------------|--------------------------------------|\n| `User.`          | Yes               | User-exposed; main runtime override  |\n| `System.`        | No (read-only)    | System-level built-ins (Age, DeltaTime, etc.) |\n| `Emitter.`       | No (internal)     | Per-emitter variables                |\n| `Particle.`      | No (internal)     | Per-particle variables               |\n\nSee `references\u002Fniagara-parameter-types.md` for the full C++ type to Niagara type mapping.\n\n---\n\n## Data Interfaces from C++\n\nData interfaces (DIs) are `UObject`-derived assets that expose structured external data to Niagara\nscripts. They appear as `User.*` parameters of DI type in the Niagara editor, and are overridden\nat runtime via `SetVariableObject` or the specialized function library helpers.\n\n### Binding Skeletal Mesh DI\n\n```cpp\n#include \"NiagaraFunctionLibrary.h\"\n\n\u002F\u002F Override the \"User.SourceMesh\" skeletal mesh DI on a running component.\nUNiagaraFunctionLibrary::OverrideSystemUserVariableSkeletalMeshComponent(\n    NiagaraComp,\n    TEXT(\"User.SourceMesh\"),    \u002F\u002F must match the DI's User parameter name in the asset\n    GetMesh()                   \u002F\u002F USkeletalMeshComponent*\n);\n\n\u002F\u002F Restrict which bones spawn from (destructive — modifies the DI instance data).\nUNiagaraFunctionLibrary::SetSkeletalMeshDataInterfaceFilteredBones(\n    NiagaraComp,\n    TEXT(\"User.SourceMesh\"),\n    { FName(\"hand_l\"), FName(\"hand_r\") }\n);\n\n\u002F\u002F Restrict which sampling regions to use.\nUNiagaraFunctionLibrary::SetSkeletalMeshDataInterfaceSamplingRegions(\n    NiagaraComp,\n    TEXT(\"User.SourceMesh\"),\n    { FName(\"HeadRegion\") }\n);\n```\n\n### Binding Static Mesh DI\n\n```cpp\n\u002F\u002F Override via component reference.\nUNiagaraFunctionLibrary::OverrideSystemUserVariableStaticMeshComponent(\n    NiagaraComp,\n    TEXT(\"User.ScatterMesh\"),\n    StaticMeshComp\n);\n\n\u002F\u002F Override with a raw UStaticMesh asset pointer.\nUNiagaraFunctionLibrary::OverrideSystemUserVariableStaticMesh(\n    NiagaraComp,\n    TEXT(\"User.ScatterMesh\"),\n    LoadedStaticMesh\n);\n```\n\n### Reading \u002F Modifying an Array DI at Runtime\n\n```cpp\n#include \"NiagaraDataInterfaceArrayFunctionLibrary.h\"\n\n\u002F\u002F Push a new float array into the effect (e.g., damage heatmap values).\nTArray\u003Cfloat> HeatValues = ComputeHeatValues();\nUNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayFloat(\n    NiagaraComp, FName(\"User.HeatData\"), HeatValues\n);\n\n\u002F\u002F Update a single element without replacing the whole array.\nUNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayFloatValue(\n    NiagaraComp, FName(\"User.HeatData\"), \u002F*Index=*\u002F 5, \u002F*Value=*\u002F 0.9f, \u002F*bSizeToFit=*\u002F false\n);\n\n\u002F\u002F Other strongly-typed array setters available:\n\u002F\u002F SetNiagaraArrayVector, SetNiagaraArrayVector4, SetNiagaraArrayColor,\n\u002F\u002F SetNiagaraArrayQuat, SetNiagaraArrayInt32, SetNiagaraArrayBool, etc.\n```\n\n### Direct DI Object Access (Advanced)\n\n```cpp\n\u002F\u002F Retrieve the actual DI UObject to mutate its properties directly.\n\u002F\u002F Template variant resolves the cast automatically.\nUNiagaraDataInterfaceCurve* CurveDI =\n    UNiagaraFunctionLibrary::GetDataInterface\u003CUNiagaraDataInterfaceCurve>(\n        NiagaraComp, FName(\"User.SpeedCurve\")\n    );\n\nif (CurveDI)\n{\n    \u002F\u002F Mutate curve keyframes at runtime (rebuilds LUT internally).\n    CurveDI->Curve.AddKey(0.f, 0.f);\n    CurveDI->Curve.AddKey(1.f, 500.f);\n    \u002F\u002F UpdateLUT() is WITH_EDITORONLY_DATA — only call in editor builds.\n#if WITH_EDITORONLY_DATA\n    CurveDI->UpdateLUT();\n#endif\n}\n\n\u002F\u002F Non-template variant when the DI class is only known at runtime.\nUNiagaraDataInterface* GenericDI =\n    UNiagaraFunctionLibrary::GetDataInterface(\n        UNiagaraDataInterfaceStaticMesh::StaticClass(),\n        NiagaraComp,\n        FName(\"User.ImpactMesh\")\n    );\n```\n\nSee `references\u002Fniagara-data-interfaces.md` for the full built-in DI catalogue.\n\n**Custom Data Interfaces**: Subclass `UNiagaraDataInterface`, override `GetFunctions()` to define\navailable functions, `GetVMExternalFunction()` to bind C++ implementations, and optionally\n`ProvidePerInstanceDataForRenderThread()` for GPU access. Register in the module's `StartupModule`.\nThis enables game-specific data (inventory, terrain) to feed directly into Niagara systems.\n\n---\n\n## Completion Callbacks\n\n```cpp\n\u002F\u002F Bind a C++ delegate to fire when the Niagara system finishes all particles.\n\u002F\u002F FOnNiagaraSystemFinished is DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(, UNiagaraComponent*)\nNiagaraComp->OnSystemFinished.AddDynamic(this, &UMyComponent::OnVFXFinished);\n\n\u002F\u002F The callback signature:\nUFUNCTION()\nvoid UMyComponent::OnVFXFinished(UNiagaraComponent* FinishedComponent)\n{\n    \u002F\u002F Called on game thread when every particle has expired and the system is done.\n    FinishedComponent->DestroyComponent();\n    \u002F\u002F or return it to pool, notify gameplay, etc.\n}\n\n\u002F\u002F Unbind when the owner is destroyed to avoid stale delegates.\nNiagaraComp->OnSystemFinished.RemoveDynamic(this, &UMyComponent::OnVFXFinished);\n```\n\n---\n\n## Performance: Pooling\n\n`ENCPoolMethod` controls the pool behavior on every spawn call:\n\n- `AutoRelease` — component returns to the world pool automatically when the system finishes.\n  Pass `bAutoDestroy=true`; the pool handles actual reclaim.\n- `ManualRelease` — you control when the component returns; call `ReleaseToPool()` to reclaim.\n- `None` — no pooling; component is destroyed when finished if `bAutoDestroy=true`.\n\n```cpp\n\u002F\u002F AutoRelease: most common for one-shots (explosions, impacts).\nUNiagaraComponent* Comp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(\n    this, ExplosionSystem, Location, FRotator::ZeroRotator,\n    FVector(1.f), \u002F*bAutoDestroy=*\u002Ftrue, \u002F*bAutoActivate=*\u002Ftrue,\n    ENCPoolMethod::AutoRelease\n);\n\n\u002F\u002F ManualRelease: for effects you pause\u002Fresume (e.g., a beam while a button is held).\n\u002F\u002F Reclaim by calling ReleaseToPool() when done.\nTrailComp->ReleaseToPool();\n\n\u002F\u002F Prime the pool before a gameplay-critical moment via FNiagaraWorldManager.\nif (FNiagaraWorldManager* NiagaraWorldMan = FNiagaraWorldManager::Get(GetWorld()))\n{\n    NiagaraWorldMan->GetComponentPool()->PrimePool(ExplosionSystem, GetWorld());\n}\n```\n\nPool capacity is configured per-system in the `UNiagaraSystem` pooling settings (not a global CVar).\nRelevant global pool CVars: `FX.NiagaraComponentPool.Enable` (1\u002F0) and\n`FX.NiagaraComponentPool.KillUnusedTime` (seconds before idle components are culled).\n\n---\n\n## Performance: Scalability and LOD\n\n```cpp\n\u002F\u002F Allow the scalability manager to cull this component based on distance and budget.\nNiagaraComp->SetAllowScalability(true);   \u002F\u002F default true; disable for gameplay-critical VFX\n\n\u002F\u002F Adjust tick behavior to avoid unnecessary dependency resolution.\n\u002F\u002F ENiagaraTickBehavior::UsePrereqs  — default; ticks after its prerequisites\n\u002F\u002F ENiagaraTickBehavior::ForceTickFirst — useful for VFX that leads all tick groups\nNiagaraComp->SetTickBehavior(ENiagaraTickBehavior::UsePrereqs);\n```\n\nScalability per platform is configured in the `UNiagaraEffectType` asset assigned to the\n`UNiagaraSystem`. The effect type defines quality tiers (Low \u002F Medium \u002F High \u002F Epic) and which\nemitters are stripped at each tier. This is data-driven; no C++ changes needed per platform.\n\n**GPU vs CPU simulation trade-offs**:\n- CPU sim: particle data is readable\u002Fwritable from C++ each frame; lower particle counts; supports\n  all DI types.\n- GPU sim: supports hundreds of thousands of particles; DI support is limited (not all CPU-side DIs\n  have GPU equivalents); particle data is not readable back to CPU without readbacks.\n\n**Determinism**: GPU simulations are inherently non-deterministic. For multiplayer VFX that must\nmatch across clients, use CPU simulation with `FixedTickDelta` on the emitter. Cosmetic-only\neffects should spawn client-side only — skip them on dedicated servers entirely.\n\n---\n\n## Warm-Up, Server Handling, and Events\n\n**Pre-simulation (warm-up)**: seek to a desired age before the effect is visible.\n```cpp\nNiagaraComp->SetDesiredAge(2.5f);    \u002F\u002F age in seconds\nNiagaraComp->SeekToDesiredAge(2.5f); \u002F\u002F perform seek immediately (skips simulation steps)\n\u002F\u002F FFXSystemSpawnParameters (used by SpawnSystemAtLocationWithParams) also exposes DesiredAge.\n```\n\n**Dedicated server**: `SpawnSystemAtLocation` returns `nullptr` on dedicated servers. Always null-check\nthe returned component and guard VFX spawns with `!IsRunningDedicatedServer()` where needed.\n\n**Gameplay events to Niagara**: Niagara's internal event system (Location Events, Death Events,\nCollision Events) is configured in the Niagara editor between emitters. From C++, trigger a\ngameplay-driven burst by updating a User bool parameter that the spawn script reads:\n```cpp\nNiagaraComp->SetVariableBool(FName(\"User.bJustDied\"), true);\n\u002F\u002F Niagara reads this flag on the next spawn script tick and fires the burst.\n\u002F\u002F There is no C++ API to inject raw Niagara events directly — use User parameters as the bridge.\n```\n\n---\n\n## Required Build.cs\n\n```csharp\nPublicDependencyModuleNames.AddRange(new string[]\n{\n    \"Core\",\n    \"CoreUObject\",\n    \"Engine\",\n    \"Niagara\",         \u002F\u002F UNiagaraComponent, UNiagaraFunctionLibrary, UNiagaraSystem\n    \"NiagaraCore\",     \u002F\u002F UNiagaraDataInterface base (NiagaraCore module)\n});\n```\n\n---\n\n## Common Mistakes and Anti-Patterns\n\n**Spawning a new system component every tick**\n```cpp\n\u002F\u002F BAD: Creates a new UNiagaraComponent each frame. Destroys performance.\nvoid AMyActor::Tick(float DeltaTime)\n{\n    UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, TrailFX, GetActorLocation());\n}\n\n\u002F\u002F GOOD: Create the component once in BeginPlay or constructor; activate\u002Fdeactivate as needed.\n```\n\n**Wrong parameter namespace**\n```cpp\n\u002F\u002F BAD: \"Emitter.Speed\" is an internal emitter parameter; cannot be set from C++.\nNiagaraComp->SetVariableFloat(FName(\"Emitter.Speed\"), 300.f);\n\n\u002F\u002F GOOD: The Niagara author must expose the parameter under \"User.*\".\nNiagaraComp->SetVariableFloat(FName(\"User.Speed\"), 300.f);\n```\n\n**Type mismatch between C++ and Niagara**\n```cpp\n\u002F\u002F BAD: Calling SetVariableVec3 on a parameter that is typed as \"Color\" in Niagara.\n\u002F\u002F Silently fails — no runtime error, parameter is just not updated.\nNiagaraComp->SetVariableVec3(FName(\"User.TintColor\"), FVector(1, 0, 0));\n\n\u002F\u002F GOOD: Match the C++ call to the Niagara parameter type.\nNiagaraComp->SetVariableLinearColor(FName(\"User.TintColor\"), FLinearColor::Red);\n```\n\n**Setting parameters after system completes**\n```cpp\n\u002F\u002F The component is valid but the system instance may be inactive. Check before setting.\nif (NiagaraComp && NiagaraComp->IsActive())\n{\n    NiagaraComp->SetVariableFloat(FName(\"User.Intensity\"), NewIntensity);\n}\n```\n\n**Forgetting to check nullptr on spawn (especially on server)**\n```cpp\nUNiagaraComponent* Comp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(...);\n\u002F\u002F Comp can be nullptr on dedicated server or when bPreCullCheck rejects the spawn.\nif (Comp)\n{\n    Comp->SetVariableFloat(FName(\"User.Scale\"), 2.f);\n}\n```\n\n**Not removing delegates before destruction**\n```cpp\n\u002F\u002F BAD: OnSystemFinished fires after owner is garbage collected → crash.\n\u002F\u002F GOOD: Always RemoveDynamic in BeginDestroy or EndPlay.\nvoid AMyActor::EndPlay(const EEndPlayReason::Type Reason)\n{\n    if (NiagaraComp)\n    {\n        NiagaraComp->OnSystemFinished.RemoveDynamic(this, &AMyActor::OnVFXFinished);\n    }\n    Super::EndPlay(Reason);\n}\n```\n\n**Niagara Fluids (experimental)**: The Niagara Fluids plugin provides GPU-based fluid and gas\nsimulations. It is experimental, GPU-only, and carries a high performance cost — profile carefully\nbefore shipping and restrict use to hero effects where visual impact justifies the budget.\n\n**World space vs local space**: Use local-space simulation for effects attached to moving actors\n(particles inherit the parent component's transform and move with it). Use world-space simulation\nfor effects that should remain stationary after emission (e.g., a ground impact crater where\nparticles should not follow a moving actor). The space setting lives on each emitter in the Niagara editor.\n\n---\n\n## Related Skills\n\n- `ue-actor-component-architecture` — component creation, attachment, lifecycle\n- `ue-materials-rendering` — particle material setup, dynamic material instances\n- `ue-cpp-foundations` — UObject lifetime, delegates, UPROPERTY references\n",{},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Fquodsoler\u002Funreal-engine-skills\u002Fue-niagara-effects",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":7,"skillName":5,"commands":33,"prompt":43,"zipAvailable":44},[34,37,40],{"tool":35,"command":36},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill ue-niagara-effects",{"tool":38,"command":39},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill ue-niagara-effects",{"tool":41,"command":42},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill ue-niagara-effects","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fquodsoler\u002Funreal-engine-skills\u002Fue-niagara-effects\u002FSKILL.md",true,[],[47,61,71,80,86,93],{"skillNo":48,"name":49,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":50,"categories":51,"keywords":57,"publishedAt":19},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[52],{"categoryNo":53,"categoryName":54,"taxonomyType":55,"taxonomyTypeDesc":56},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[58,59,60],"model","model hierarchy","model hierarchy skill",{"skillNo":62,"name":63,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":67,"publishedAt":19},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[66],{"categoryNo":53,"categoryName":54,"taxonomyType":55,"taxonomyTypeDesc":56},[68,69,70],"MCP","zoom","zoom mcp",{"skillNo":72,"name":73,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":74,"categories":75,"keywords":76,"publishedAt":19},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[77,78,79,69],"sdk","zoom apps sdk","apps",{"skillNo":81,"name":82,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":85,"publishedAt":19},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[82],{"skillNo":87,"name":88,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":89,"categories":90,"keywords":92,"publishedAt":19},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[91],{"categoryNo":53,"categoryName":54,"taxonomyType":55,"taxonomyTypeDesc":56},[88],{"skillNo":94,"name":95,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":7,"ownerLogin":8,"ownerAvatarUrl":9,"ownerAccountNo":10,"ownerDisplayName":11,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":98,"publishedAt":19},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[95]]