ue-niagara-effects
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUE Niagara Effects
UE Niagara 特效
You are an expert in controlling Unreal Engine's Niagara VFX system from C++.
你是一位精通通过C++控制Unreal Engine Niagara VFX系统的专家。
Context Check
上下文检查
Read before proceeding. Confirm:
.agents/ue-project-context.md- The plugin is listed under enabled plugins (
Niagara).Plugins/FX/Niagara - The target module's has
Build.cs(and optionally"Niagara") in"NiagaraCore".PublicDependencyModuleNames - Platform targets: note whether mobile or dedicated-server builds are in scope, because Niagara is typically suppressed on dedicated servers and may need LOD simplification on mobile.
在继续操作前,请阅读。确认:
.agents/ue-project-context.md- 插件已在启用插件列表中(
Niagara)。Plugins/FX/Niagara - 目标模块的中,
Build.cs包含PublicDependencyModuleNames(可选包含"Niagara")。"NiagaraCore" - 平台目标:注意是否涉及移动端或专用服务器构建,因为Niagara在专用服务器上通常会被禁用,且在移动端可能需要LOD简化。
Information Gathering
信息收集
Before writing Niagara C++ code, clarify:
- Effect lifecycle — one-shot (fire and forget) or persistent / looping?
- Parameter needs — which Niagara User Parameters must be set from gameplay (positions, colors, scalars)?
- Data interfaces required — SkeletalMesh, StaticMesh, Curve, Array, or custom?
- Simulation target — CPU or GPU sim? (affects which DI features are available)
- Performance budget — pooling required? Mobile scalability tier?
- Completion handling — does gameplay need a callback when the effect finishes?
在编写Niagara C++代码前,请明确以下内容:
- 特效生命周期 — 一次性(触发后无需关注)还是持久/循环型?
- 参数需求 — 哪些Niagara用户参数需要从 gameplay 设置(位置、颜色、标量)?
- 所需数据接口 — SkeletalMesh、StaticMesh、Curve、Array还是自定义接口?
- 模拟目标 — CPU还是GPU模拟?(会影响可用的DI功能)
- 性能预算 — 是否需要对象池?移动端可伸缩性等级?
- 完成处理 — 特效结束时,gameplay是否需要回调?
System Structure (UE Concept Map)
系统结构(UE概念图)
UNiagaraSystem (asset: UNiagaraSystem)
└── UNiagaraEmitter[] (per-emitter asset, referenced via FNiagaraEmitterHandle)
└── UNiagaraScript[] (Spawn / Update / Event scripts; authored in Niagara editor)
└── Modules (stack of NiagaraScript nodes; not C++ classes)
Runtime instances:
UNiagaraComponent (scene component that drives one UNiagaraSystem instance)
└── FNiagaraSystemInstance (internal runtime state; access via GetSystemInstanceController())Key rule: authors expose parameters to C++ by setting their namespace to in the Niagara
editor. Only parameters can be overridden at runtime from C++.
User.User.*UNiagaraSystem (资源: UNiagaraSystem)
└── UNiagaraEmitter[] (每个发射器的资源,通过FNiagaraEmitterHandle引用)
└── UNiagaraScript[] (生成/更新/事件脚本;在Niagara编辑器中创建)
└── Modules (NiagaraScript节点栈;非C++类)
运行时实例:
UNiagaraComponent (驱动单个UNiagaraSystem实例的场景组件)
└── FNiagaraSystemInstance (内部运行时状态;通过GetSystemInstanceController()访问)核心规则:开发者在Niagara编辑器中将参数的命名空间设置为,以此向C++暴露参数。只有参数能在运行时通过C++覆盖。
User.User.*Spawning Niagara Systems
生成Niagara系统
Fire-and-Forget (One-Shot) at World Location
在世界位置生成一次性特效
cpp
#include "NiagaraFunctionLibrary.h"
#include "NiagaraComponent.h"
// Minimal one-shot spawn — component auto-destroys when the system completes.
UNiagaraComponent* NiagaraComp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
this, // WorldContextObject
ImpactVFXSystem, // UPROPERTY(EditAnywhere) UNiagaraSystem*
HitLocation, // FVector Location
FRotator::ZeroRotator, // FRotator Rotation
FVector(1.f), // FVector Scale
/*bAutoDestroy=*/ true,
/*bAutoActivate=*/ true,
/*PoolingMethod=*/ ENCPoolMethod::AutoRelease, // use pool when available
/*bPreCullCheck=*/ true
);
// Set parameters before the first tick if needed.
if (NiagaraComp)
{
NiagaraComp->SetVariableVec3(FName("User.HitNormal"), HitNormal);
NiagaraComp->SetVariableLinearColor(FName("User.HitColor"), DamageColor);
}cpp
#include "NiagaraFunctionLibrary.h"
#include "NiagaraComponent.h"
// 最简一次性生成 — 系统完成后组件自动销毁。
UNiagaraComponent* NiagaraComp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
this, // WorldContextObject
ImpactVFXSystem, // UPROPERTY(EditAnywhere) UNiagaraSystem*
HitLocation, // FVector Location
FRotator::ZeroRotator, // FRotator Rotation
FVector(1.f), // FVector Scale
/*bAutoDestroy=*/ true,
/*bAutoActivate=*/ true,
/*PoolingMethod=*/ ENCPoolMethod::AutoRelease, // 可用时使用对象池
/*bPreCullCheck=*/ true
);
// 如需在第一帧前设置参数
if (NiagaraComp)
{
NiagaraComp->SetVariableVec3(FName("User.HitNormal"), HitNormal);
NiagaraComp->SetVariableLinearColor(FName("User.HitColor"), DamageColor);
}Attached to a Component (Persistent / Looping)
附着到组件(持久/循环型)
cpp
// Attaches to a socket and stays active until manually deactivated.
UNiagaraComponent* TrailComp = UNiagaraFunctionLibrary::SpawnSystemAttached(
TrailVFXSystem,
WeaponMesh, // USceneComponent* AttachToComponent
FName("MuzzleSocket"), // FName AttachPointName
FVector::ZeroVector,
FRotator::ZeroRotator,
EAttachLocation::SnapToTarget,
/*bAutoDestroy=*/ false,
/*bAutoActivate=*/ true,
ENCPoolMethod::ManualRelease,
/*bPreCullCheck=*/ true
);cpp
// 附着到插槽并保持激活,直到手动停用。
UNiagaraComponent* TrailComp = UNiagaraFunctionLibrary::SpawnSystemAttached(
TrailVFXSystem,
WeaponMesh, // USceneComponent* AttachToComponent
FName("MuzzleSocket"), // FName AttachPointName
FVector::ZeroVector,
FRotator::ZeroRotator,
EAttachLocation::SnapToTarget,
/*bAutoDestroy=*/ false,
/*bAutoActivate=*/ true,
ENCPoolMethod::ManualRelease,
/*bPreCullCheck=*/ true
);Persistent Component on an Actor (Preferred for Repeated Use)
Actor上的持久组件(推荐用于重复使用)
cpp
// In header:
UPROPERTY(VisibleAnywhere)
TObjectPtr<UNiagaraComponent> EngineTrailVFX;
// In constructor:
EngineTrailVFX = CreateDefaultSubobject<UNiagaraComponent>(TEXT("EngineTrailVFX"));
EngineTrailVFX->SetupAttachment(GetRootComponent());
EngineTrailVFX->SetAutoActivate(false); // start inactive; activate via gameplay
// In gameplay code:
EngineTrailVFX->SetAsset(EngineTrailSystem); // swap asset without destroying component
EngineTrailVFX->Activate(/*bReset=*/ true);cpp
// 在头文件中:
UPROPERTY(VisibleAnywhere)
TObjectPtr<UNiagaraComponent> EngineTrailVFX;
// 在构造函数中:
EngineTrailVFX = CreateDefaultSubobject<UNiagaraComponent>(TEXT("EngineTrailVFX"));
EngineTrailVFX->SetupAttachment(GetRootComponent());
EngineTrailVFX->SetAutoActivate(false); // 初始为非激活状态;通过gameplay激活
// 在gameplay代码中:
EngineTrailVFX->SetAsset(EngineTrailSystem); // 替换资源而不销毁组件
EngineTrailVFX->Activate(/*bReset=*/ true);Lifecycle Control
生命周期控制
cpp
NiagaraComp->Activate(/*bReset=*/ false); // activate; resume if paused
NiagaraComp->Activate(/*bReset=*/ true); // activate with full reset
NiagaraComp->Deactivate(); // stop spawning, let particles drain
NiagaraComp->DeactivateImmediate(); // kill all particles immediately
NiagaraComp->ResetSystem(); // restart from time 0
NiagaraComp->ReinitializeSystem(); // full re-init + restart (expensive; prefer ResetSystem)
NiagaraComp->SetPaused(true); // pause simulation
NiagaraComp->SetAutoDestroy(true); // destroy component when system finishescpp
NiagaraComp->Activate(/*bReset=*/ false); // 激活;若已暂停则恢复
NiagaraComp->Activate(/*bReset=*/ true); // 激活并完全重置
NiagaraComp->Deactivate(); // 停止生成粒子,让现有粒子自然消失
NiagaraComp->DeactivateImmediate(); // 立即销毁所有粒子
NiagaraComp->ResetSystem(); // 从时间0重启
NiagaraComp->ReinitializeSystem(); // 完全重新初始化并重启(开销大;优先使用ResetSystem)
NiagaraComp->SetPaused(true); // 暂停模拟
NiagaraComp->SetAutoDestroy(true); // 系统完成时销毁组件Setting Parameters from C++
通过C++设置参数
All setter variants accept the parameter name as prefixed with its namespace.
User-exposed parameters use the prefix.
FNameUser.cpp
// Scalar types
NiagaraComp->SetVariableFloat(FName("User.DamageAmount"), 150.f);
NiagaraComp->SetVariableInt(FName("User.ProjectileCount"), 12);
NiagaraComp->SetVariableBool(FName("User.bIsCritical"), bIsCriticalHit);
// Vector types
NiagaraComp->SetVariableVec2(FName("User.UVOffset"), FVector2D(0.5, 0.25));
NiagaraComp->SetVariableVec3(FName("User.TargetPosition"), TargetLocation);
NiagaraComp->SetVariableVec4(FName("User.CustomData"), FVector4(1, 0.5, 0, 1));
NiagaraComp->SetVariableLinearColor(FName("User.TintColor"), FLinearColor::Red);
NiagaraComp->SetVariableQuat(FName("User.Orientation"), GetActorQuat());
// Object / Actor references (binds DI override)
NiagaraComp->SetVariableObject(FName("User.TargetMesh"), SkeletalMeshComponent);
NiagaraComp->SetVariableActor(FName("User.SourceActor"), this);
// Position (LWC-aware alias for vec3)
NiagaraComp->SetVariablePosition(FName("User.WorldOrigin"), WorldSpaceOrigin);
// Material / Texture overrides
NiagaraComp->SetVariableMaterial(FName("User.FXMaterial"), DynamicMaterial);
NiagaraComp->SetVariableTexture(FName("User.FlowMap"), FlowTexture);
// Read a float parameter back (returns bIsValid=false when name not found)
bool bIsValid = false;
float CurrentValue = NiagaraComp->GetVariableFloat(FName("User.EmitRate"), bIsValid);所有设置方法都接受带命名空间前缀的作为参数名。用户暴露的参数使用前缀。
FNameUser.cpp
// 标量类型
NiagaraComp->SetVariableFloat(FName("User.DamageAmount"), 150.f);
NiagaraComp->SetVariableInt(FName("User.ProjectileCount"), 12);
NiagaraComp->SetVariableBool(FName("User.bIsCritical"), bIsCriticalHit);
// 向量类型
NiagaraComp->SetVariableVec2(FName("User.UVOffset"), FVector2D(0.5, 0.25));
NiagaraComp->SetVariableVec3(FName("User.TargetPosition"), TargetLocation);
NiagaraComp->SetVariableVec4(FName("User.CustomData"), FVector4(1, 0.5, 0, 1));
NiagaraComp->SetVariableLinearColor(FName("User.TintColor"), FLinearColor::Red);
NiagaraComp->SetVariableQuat(FName("User.Orientation"), GetActorQuat());
// 对象/Actor引用(绑定DI覆盖)
NiagaraComp->SetVariableObject(FName("User.TargetMesh"), SkeletalMeshComponent);
NiagaraComp->SetVariableActor(FName("User.SourceActor"), this);
// 位置(支持LWC的vec3别名)
NiagaraComp->SetVariablePosition(FName("User.WorldOrigin"), WorldSpaceOrigin);
// 材质/纹理覆盖
NiagaraComp->SetVariableMaterial(FName("User.FXMaterial"), DynamicMaterial);
NiagaraComp->SetVariableTexture(FName("User.FlowMap"), FlowTexture);
// 读取float参数(名称不存在时返回bIsValid=false)
bool bIsValid = false;
float CurrentValue = NiagaraComp->GetVariableFloat(FName("User.EmitRate"), bIsValid);Blueprint-Accessible Legacy Signatures (prefer FName variants above)
可用于蓝图的旧版签名(优先使用上述FName版本)
cpp
// Old FString signatures still work but are slower due to FName conversion.
NiagaraComp->SetNiagaraVariableFloat(TEXT("User.SpeedScale"), 2.f);
NiagaraComp->SetNiagaraVariableVec3(TEXT("User.ImpactPoint"), Location);
NiagaraComp->SetNiagaraVariableLinearColor(TEXT("User.Color"), FLinearColor::Blue);cpp
// 旧版FString签名仍可使用,但因FName转换会更慢。
NiagaraComp->SetNiagaraVariableFloat(TEXT("User.SpeedScale"), 2.f);
NiagaraComp->SetNiagaraVariableVec3(TEXT("User.ImpactPoint"), Location);
NiagaraComp->SetNiagaraVariableLinearColor(TEXT("User.Color"), FLinearColor::Blue);Parameter Namespaces Reference
参数命名空间参考
| Namespace prefix | Settable from C++ | Description |
|---|---|---|
| Yes | User-exposed; main runtime override |
| No (read-only) | System-level built-ins (Age, DeltaTime, etc.) |
| No (internal) | Per-emitter variables |
| No (internal) | Per-particle variables |
See for the full C++ type to Niagara type mapping.
references/niagara-parameter-types.md| 命名空间前缀 | 是否可通过C++设置 | 描述 |
|---|---|---|
| 是 | 用户暴露的参数;主要用于运行时覆盖 |
| 否(只读) | 系统级内置参数(如Age、DeltaTime等) |
| 否(内部) | 每个发射器的变量 |
| 否(内部) | 每个粒子的变量 |
完整的C++类型与Niagara类型映射,请查看。
references/niagara-parameter-types.mdData Interfaces from C++
通过C++操作数据接口
Data interfaces (DIs) are -derived assets that expose structured external data to Niagara
scripts. They appear as parameters of DI type in the Niagara editor, and are overridden
at runtime via or the specialized function library helpers.
UObjectUser.*SetVariableObject数据接口(DI)是继承自的资源,用于向Niagara脚本暴露结构化的外部数据。它们在Niagara编辑器中显示为DI类型的参数,可在运行时通过或专用函数库进行覆盖。
UObjectUser.*SetVariableObjectBinding Skeletal Mesh DI
绑定Skeletal Mesh DI
cpp
#include "NiagaraFunctionLibrary.h"
// Override the "User.SourceMesh" skeletal mesh DI on a running component.
UNiagaraFunctionLibrary::OverrideSystemUserVariableSkeletalMeshComponent(
NiagaraComp,
TEXT("User.SourceMesh"), // must match the DI's User parameter name in the asset
GetMesh() // USkeletalMeshComponent*
);
// Restrict which bones spawn from (destructive — modifies the DI instance data).
UNiagaraFunctionLibrary::SetSkeletalMeshDataInterfaceFilteredBones(
NiagaraComp,
TEXT("User.SourceMesh"),
{ FName("hand_l"), FName("hand_r") }
);
// Restrict which sampling regions to use.
UNiagaraFunctionLibrary::SetSkeletalMeshDataInterfaceSamplingRegions(
NiagaraComp,
TEXT("User.SourceMesh"),
{ FName("HeadRegion") }
);cpp
#include "NiagaraFunctionLibrary.h"
// 在运行中的组件上覆盖"User.SourceMesh"骨骼网格DI。
UNiagaraFunctionLibrary::OverrideSystemUserVariableSkeletalMeshComponent(
NiagaraComp,
TEXT("User.SourceMesh"), // 必须与资源中DI的User参数名称匹配
GetMesh() // USkeletalMeshComponent*
);
// 限制生成粒子的骨骼(会修改DI实例数据)。
UNiagaraFunctionLibrary::SetSkeletalMeshDataInterfaceFilteredBones(
NiagaraComp,
TEXT("User.SourceMesh"),
{ FName("hand_l"), FName("hand_r") }
);
// 限制使用的采样区域。
UNiagaraFunctionLibrary::SetSkeletalMeshDataInterfaceSamplingRegions(
NiagaraComp,
TEXT("User.SourceMesh"),
{ FName("HeadRegion") }
);Binding Static Mesh DI
绑定Static Mesh DI
cpp
// Override via component reference.
UNiagaraFunctionLibrary::OverrideSystemUserVariableStaticMeshComponent(
NiagaraComp,
TEXT("User.ScatterMesh"),
StaticMeshComp
);
// Override with a raw UStaticMesh asset pointer.
UNiagaraFunctionLibrary::OverrideSystemUserVariableStaticMesh(
NiagaraComp,
TEXT("User.ScatterMesh"),
LoadedStaticMesh
);cpp
// 通过组件引用覆盖。
UNiagaraFunctionLibrary::OverrideSystemUserVariableStaticMeshComponent(
NiagaraComp,
TEXT("User.ScatterMesh"),
StaticMeshComp
);
// 使用原始UStaticMesh资源指针覆盖。
UNiagaraFunctionLibrary::OverrideSystemUserVariableStaticMesh(
NiagaraComp,
TEXT("User.ScatterMesh"),
LoadedStaticMesh
);Reading / Modifying an Array DI at Runtime
在运行时读取/修改Array DI
cpp
#include "NiagaraDataInterfaceArrayFunctionLibrary.h"
// Push a new float array into the effect (e.g., damage heatmap values).
TArray<float> HeatValues = ComputeHeatValues();
UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayFloat(
NiagaraComp, FName("User.HeatData"), HeatValues
);
// Update a single element without replacing the whole array.
UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayFloatValue(
NiagaraComp, FName("User.HeatData"), /*Index=*/ 5, /*Value=*/ 0.9f, /*bSizeToFit=*/ false
);
// Other strongly-typed array setters available:
// SetNiagaraArrayVector, SetNiagaraArrayVector4, SetNiagaraArrayColor,
// SetNiagaraArrayQuat, SetNiagaraArrayInt32, SetNiagaraArrayBool, etc.cpp
#include "NiagaraDataInterfaceArrayFunctionLibrary.h"
// 将新的float数组传入特效(如伤害热图数据)。
TArray<float> HeatValues = ComputeHeatValues();
UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayFloat(
NiagaraComp, FName("User.HeatData"), HeatValues
);
// 更新单个元素而不替换整个数组。
UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayFloatValue(
NiagaraComp, FName("User.HeatData"), /*Index=*/ 5, /*Value=*/ 0.9f, /*bSizeToFit=*/ false
);
// 其他强类型数组设置方法可用:
// SetNiagaraArrayVector, SetNiagaraArrayVector4, SetNiagaraArrayColor,
// SetNiagaraArrayQuat, SetNiagaraArrayInt32, SetNiagaraArrayBool等。Direct DI Object Access (Advanced)
直接访问DI对象(进阶)
cpp
// Retrieve the actual DI UObject to mutate its properties directly.
// Template variant resolves the cast automatically.
UNiagaraDataInterfaceCurve* CurveDI =
UNiagaraFunctionLibrary::GetDataInterface<UNiagaraDataInterfaceCurve>(
NiagaraComp, FName("User.SpeedCurve")
);
if (CurveDI)
{
// Mutate curve keyframes at runtime (rebuilds LUT internally).
CurveDI->Curve.AddKey(0.f, 0.f);
CurveDI->Curve.AddKey(1.f, 500.f);
// UpdateLUT() is WITH_EDITORONLY_DATA — only call in editor builds.
#if WITH_EDITORONLY_DATA
CurveDI->UpdateLUT();
#endif
}
// Non-template variant when the DI class is only known at runtime.
UNiagaraDataInterface* GenericDI =
UNiagaraFunctionLibrary::GetDataInterface(
UNiagaraDataInterfaceStaticMesh::StaticClass(),
NiagaraComp,
FName("User.ImpactMesh")
);See for the full built-in DI catalogue.
references/niagara-data-interfaces.mdCustom Data Interfaces: Subclass , override to define
available functions, to bind C++ implementations, and optionally
for GPU access. Register in the module's .
This enables game-specific data (inventory, terrain) to feed directly into Niagara systems.
UNiagaraDataInterfaceGetFunctions()GetVMExternalFunction()ProvidePerInstanceDataForRenderThread()StartupModulecpp
// 获取实际的DI UObject以直接修改其属性。
// 模板版本会自动处理类型转换。
UNiagaraDataInterfaceCurve* CurveDI =
UNiagaraFunctionLibrary::GetDataInterface<UNiagaraDataInterfaceCurve>(
NiagaraComp, FName("User.SpeedCurve")
);
if (CurveDI)
{
// 在运行时修改曲线关键帧(内部会重建LUT)。
CurveDI->Curve.AddKey(0.f, 0.f);
CurveDI->Curve.AddKey(1.f, 500.f);
// UpdateLUT()属于WITH_EDITORONLY_DATA — 仅在编辑器构建中调用。
#if WITH_EDITORONLY_DATA
CurveDI->UpdateLUT();
#endif
}
// 当DI类仅在运行时已知时,使用非模板版本。
UNiagaraDataInterface* GenericDI =
UNiagaraFunctionLibrary::GetDataInterface(
UNiagaraDataInterfaceStaticMesh::StaticClass(),
NiagaraComp,
FName("User.ImpactMesh")
);完整的内置DI目录,请查看。
references/niagara-data-interfaces.md自定义数据接口:继承,重写定义可用函数,重写绑定C++实现,可选重写以支持GPU访问。在模块的中注册。这可让游戏特定数据(如库存、地形)直接传入Niagara系统。
UNiagaraDataInterfaceGetFunctions()GetVMExternalFunction()ProvidePerInstanceDataForRenderThread()StartupModuleCompletion Callbacks
完成回调
cpp
// Bind a C++ delegate to fire when the Niagara system finishes all particles.
// FOnNiagaraSystemFinished is DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(, UNiagaraComponent*)
NiagaraComp->OnSystemFinished.AddDynamic(this, &UMyComponent::OnVFXFinished);
// The callback signature:
UFUNCTION()
void UMyComponent::OnVFXFinished(UNiagaraComponent* FinishedComponent)
{
// Called on game thread when every particle has expired and the system is done.
FinishedComponent->DestroyComponent();
// or return it to pool, notify gameplay, etc.
}
// Unbind when the owner is destroyed to avoid stale delegates.
NiagaraComp->OnSystemFinished.RemoveDynamic(this, &UMyComponent::OnVFXFinished);cpp
// 将C++委托绑定到Niagara系统所有粒子完成时触发的事件。
// FOnNiagaraSystemFinished的定义为DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(, UNiagaraComponent*)
NiagaraComp->OnSystemFinished.AddDynamic(this, &UMyComponent::OnVFXFinished);
// 回调函数签名:
UFUNCTION()
void UMyComponent::OnVFXFinished(UNiagaraComponent* FinishedComponent)
{
// 当所有粒子过期且系统完成时,在游戏线程调用。
FinishedComponent->DestroyComponent();
// 或将其返回对象池、通知gameplay等。
}
// 当所有者销毁时解除绑定,避免无效委托。
NiagaraComp->OnSystemFinished.RemoveDynamic(this, &UMyComponent::OnVFXFinished);Performance: Pooling
性能优化:对象池
ENCPoolMethod- — component returns to the world pool automatically when the system finishes. Pass
AutoRelease; the pool handles actual reclaim.bAutoDestroy=true - — you control when the component returns; call
ManualReleaseto reclaim.ReleaseToPool() - — no pooling; component is destroyed when finished if
None.bAutoDestroy=true
cpp
// AutoRelease: most common for one-shots (explosions, impacts).
UNiagaraComponent* Comp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
this, ExplosionSystem, Location, FRotator::ZeroRotator,
FVector(1.f), /*bAutoDestroy=*/true, /*bAutoActivate=*/true,
ENCPoolMethod::AutoRelease
);
// ManualRelease: for effects you pause/resume (e.g., a beam while a button is held).
// Reclaim by calling ReleaseToPool() when done.
TrailComp->ReleaseToPool();
// Prime the pool before a gameplay-critical moment via FNiagaraWorldManager.
if (FNiagaraWorldManager* NiagaraWorldMan = FNiagaraWorldManager::Get(GetWorld()))
{
NiagaraWorldMan->GetComponentPool()->PrimePool(ExplosionSystem, GetWorld());
}Pool capacity is configured per-system in the pooling settings (not a global CVar).
Relevant global pool CVars: (1/0) and
(seconds before idle components are culled).
UNiagaraSystemFX.NiagaraComponentPool.EnableFX.NiagaraComponentPool.KillUnusedTimeENCPoolMethod- — 系统完成后,组件自动返回世界对象池。需传入
AutoRelease;由对象池处理实际回收。bAutoDestroy=true - — 由你控制组件何时返回;调用
ManualRelease进行回收。ReleaseToPool() - — 不使用对象池;若
None,组件完成时会被销毁。bAutoDestroy=true
cpp
// AutoRelease: 最常用于一次性特效(爆炸、冲击)。
UNiagaraComponent* Comp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
this, ExplosionSystem, Location, FRotator::ZeroRotator,
FVector(1.f), /*bAutoDestroy=*/true, /*bAutoActivate=*/true,
ENCPoolMethod::AutoRelease
);
// ManualRelease: 用于需要暂停/恢复的特效(如按住按钮时的光束)。
// 完成时调用ReleaseToPool()回收。
TrailComp->ReleaseToPool();
// 在gameplay关键节点前,通过FNiagaraWorldManager预填充对象池。
if (FNiagaraWorldManager* NiagaraWorldMan = FNiagaraWorldManager::Get(GetWorld()))
{
NiagaraWorldMan->GetComponentPool()->PrimePool(ExplosionSystem, GetWorld());
}对象池容量在的对象池设置中按系统配置(非全局CVar)。相关全局对象池CVar:(1/0)和(闲置组件被剔除前的秒数)。
UNiagaraSystemFX.NiagaraComponentPool.EnableFX.NiagaraComponentPool.KillUnusedTimePerformance: Scalability and LOD
性能优化:可伸缩性与LOD
cpp
// Allow the scalability manager to cull this component based on distance and budget.
NiagaraComp->SetAllowScalability(true); // default true; disable for gameplay-critical VFX
// Adjust tick behavior to avoid unnecessary dependency resolution.
// ENiagaraTickBehavior::UsePrereqs — default; ticks after its prerequisites
// ENiagaraTickBehavior::ForceTickFirst — useful for VFX that leads all tick groups
NiagaraComp->SetTickBehavior(ENiagaraTickBehavior::UsePrereqs);Scalability per platform is configured in the asset assigned to the
. The effect type defines quality tiers (Low / Medium / High / Epic) and which
emitters are stripped at each tier. This is data-driven; no C++ changes needed per platform.
UNiagaraEffectTypeUNiagaraSystemGPU vs CPU simulation trade-offs:
- CPU sim: particle data is readable/writable from C++ each frame; lower particle counts; supports all DI types.
- GPU sim: supports hundreds of thousands of particles; DI support is limited (not all CPU-side DIs have GPU equivalents); particle data is not readable back to CPU without readbacks.
Determinism: GPU simulations are inherently non-deterministic. For multiplayer VFX that must
match across clients, use CPU simulation with on the emitter. Cosmetic-only
effects should spawn client-side only — skip them on dedicated servers entirely.
FixedTickDeltacpp
// 允许可伸缩性管理器根据距离和预算剔除该组件。
NiagaraComp->SetAllowScalability(true); // 默认开启;对gameplay关键VFX可禁用
// 调整tick行为,避免不必要的依赖解析。
// ENiagaraTickBehavior::UsePrereqs — 默认;在依赖项之后tick
// ENiagaraTickBehavior::ForceTickFirst — 适用于需要领先所有tick组的VFX
NiagaraComp->SetTickBehavior(ENiagaraTickBehavior::UsePrereqs);各平台的可伸缩性配置在分配给的资源中设置。该特效类型定义了质量等级(低/中/高/史诗)以及每个等级下会被移除的发射器。这是数据驱动的;无需针对每个平台修改C++代码。
UNiagaraSystemUNiagaraEffectTypeGPU与CPU模拟的权衡:
- CPU模拟:粒子数据每帧可从C++读写;支持的粒子数量较少;支持所有DI类型。
- GPU模拟:支持数十万粒子;DI支持有限(并非所有CPU端DI都有GPU等效版本);粒子数据无法直接读回CPU,除非使用回读操作。
确定性:GPU模拟本质上是非确定性的。对于需要在客户端间保持一致的多人游戏VFX,需使用CPU模拟并在发射器上启用。仅用于视觉效果的特效应仅在客户端生成 — 在专用服务器上完全跳过。
FixedTickDeltaWarm-Up, Server Handling, and Events
预热、服务器处理与事件
Pre-simulation (warm-up): seek to a desired age before the effect is visible.
cpp
NiagaraComp->SetDesiredAge(2.5f); // age in seconds
NiagaraComp->SeekToDesiredAge(2.5f); // perform seek immediately (skips simulation steps)
// FFXSystemSpawnParameters (used by SpawnSystemAtLocationWithParams) also exposes DesiredAge.Dedicated server: returns on dedicated servers. Always null-check
the returned component and guard VFX spawns with where needed.
SpawnSystemAtLocationnullptr!IsRunningDedicatedServer()Gameplay events to Niagara: Niagara's internal event system (Location Events, Death Events,
Collision Events) is configured in the Niagara editor between emitters. From C++, trigger a
gameplay-driven burst by updating a User bool parameter that the spawn script reads:
cpp
NiagaraComp->SetVariableBool(FName("User.bJustDied"), true);
// Niagara reads this flag on the next spawn script tick and fires the burst.
// There is no C++ API to inject raw Niagara events directly — use User parameters as the bridge.预模拟(预热):在特效可见前,将其推进到指定时长。
cpp
NiagaraComp->SetDesiredAge(2.5f); // 时长(秒)
NiagaraComp->SeekToDesiredAge(2.5f); // 立即推进(跳过模拟步骤)
// FFXSystemSpawnParameters(SpawnSystemAtLocationWithParams使用)也支持DesiredAge。专用服务器:在专用服务器上返回。始终对返回的组件进行空值检查,并在必要时使用保护VFX生成代码。
SpawnSystemAtLocationnullptr!IsRunningDedicatedServer()向Niagara发送gameplay事件:Niagara的内部事件系统(位置事件、死亡事件、碰撞事件)在Niagara编辑器中于发射器间配置。在C++中,可通过更新生成脚本读取的User布尔参数来触发gameplay驱动的粒子爆发:
cpp
NiagaraComp->SetVariableBool(FName("User.bJustDied"), true);
// Niagara会在下一次生成脚本tick时读取该标志并触发爆发。
// 目前没有C++ API可直接注入原始Niagara事件 — 使用User参数作为桥梁。Required Build.cs
所需Build.cs配置
csharp
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"Niagara", // UNiagaraComponent, UNiagaraFunctionLibrary, UNiagaraSystem
"NiagaraCore", // UNiagaraDataInterface base (NiagaraCore module)
});csharp
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"Niagara", // UNiagaraComponent、UNiagaraFunctionLibrary、UNiagaraSystem
"NiagaraCore", // UNiagaraDataInterface基类(NiagaraCore模块)
});Common Mistakes and Anti-Patterns
常见错误与反模式
Spawning a new system component every tick
cpp
// BAD: Creates a new UNiagaraComponent each frame. Destroys performance.
void AMyActor::Tick(float DeltaTime)
{
UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, TrailFX, GetActorLocation());
}
// GOOD: Create the component once in BeginPlay or constructor; activate/deactivate as needed.Wrong parameter namespace
cpp
// BAD: "Emitter.Speed" is an internal emitter parameter; cannot be set from C++.
NiagaraComp->SetVariableFloat(FName("Emitter.Speed"), 300.f);
// GOOD: The Niagara author must expose the parameter under "User.*".
NiagaraComp->SetVariableFloat(FName("User.Speed"), 300.f);Type mismatch between C++ and Niagara
cpp
// BAD: Calling SetVariableVec3 on a parameter that is typed as "Color" in Niagara.
// Silently fails — no runtime error, parameter is just not updated.
NiagaraComp->SetVariableVec3(FName("User.TintColor"), FVector(1, 0, 0));
// GOOD: Match the C++ call to the Niagara parameter type.
NiagaraComp->SetVariableLinearColor(FName("User.TintColor"), FLinearColor::Red);Setting parameters after system completes
cpp
// The component is valid but the system instance may be inactive. Check before setting.
if (NiagaraComp && NiagaraComp->IsActive())
{
NiagaraComp->SetVariableFloat(FName("User.Intensity"), NewIntensity);
}Forgetting to check nullptr on spawn (especially on server)
cpp
UNiagaraComponent* Comp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(...);
// Comp can be nullptr on dedicated server or when bPreCullCheck rejects the spawn.
if (Comp)
{
Comp->SetVariableFloat(FName("User.Scale"), 2.f);
}Not removing delegates before destruction
cpp
// BAD: OnSystemFinished fires after owner is garbage collected → crash.
// GOOD: Always RemoveDynamic in BeginDestroy or EndPlay.
void AMyActor::EndPlay(const EEndPlayReason::Type Reason)
{
if (NiagaraComp)
{
NiagaraComp->OnSystemFinished.RemoveDynamic(this, &AMyActor::OnVFXFinished);
}
Super::EndPlay(Reason);
}Niagara Fluids (experimental): The Niagara Fluids plugin provides GPU-based fluid and gas
simulations. It is experimental, GPU-only, and carries a high performance cost — profile carefully
before shipping and restrict use to hero effects where visual impact justifies the budget.
World space vs local space: Use local-space simulation for effects attached to moving actors
(particles inherit the parent component's transform and move with it). Use world-space simulation
for effects that should remain stationary after emission (e.g., a ground impact crater where
particles should not follow a moving actor). The space setting lives on each emitter in the Niagara editor.
每帧生成新的系统组件
cpp
// 错误:每帧创建新的UNiagaraComponent,严重影响性能。
void AMyActor::Tick(float DeltaTime)
{
UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, TrailFX, GetActorLocation());
}
// 正确:在BeginPlay或构造函数中创建一次组件;按需激活/停用。错误的参数命名空间
cpp
// 错误:"Emitter.Speed"是发射器内部参数,无法通过C++设置。
NiagaraComp->SetVariableFloat(FName("Emitter.Speed"), 300.f);
// 正确:Niagara开发者必须将参数暴露在"User.*"下。
NiagaraComp->SetVariableFloat(FName("User.Speed"), 300.f);C++与Niagara类型不匹配
cpp
// 错误:对Niagara中类型为"Color"的参数调用SetVariableVec3。
// 会静默失败 — 无运行时错误,但参数不会更新。
NiagaraComp->SetVariableVec3(FName("User.TintColor"), FVector(1, 0, 0));
// 正确:C++调用需匹配Niagara参数类型。
NiagaraComp->SetVariableLinearColor(FName("User.TintColor"), FLinearColor::Red);系统完成后设置参数
cpp
// 组件可能有效,但系统实例可能已停用。设置前请检查。
if (NiagaraComp && NiagaraComp->IsActive())
{
NiagaraComp->SetVariableFloat(FName("User.Intensity"), NewIntensity);
}生成时忘记检查空指针(尤其是在服务器上)
cpp
UNiagaraComponent* Comp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(...);
// Comp在专用服务器或bPreCullCheck拒绝生成时可能为nullptr。
if (Comp)
{
Comp->SetVariableFloat(FName("User.Scale"), 2.f);
}销毁前未移除委托
cpp
// 错误:OnSystemFinished在所有者被垃圾回收后触发 → 崩溃。
// 正确:始终在BeginDestroy或EndPlay中调用RemoveDynamic。
void AMyActor::EndPlay(const EEndPlayReason::Type Reason)
{
if (NiagaraComp)
{
NiagaraComp->OnSystemFinished.RemoveDynamic(this, &AMyActor::OnVFXFinished);
}
Super::EndPlay(Reason);
}Niagara流体(实验性):Niagara流体插件提供基于GPU的流体和气体模拟。它处于实验阶段,仅支持GPU,且性能开销高 — 发布前需仔细分析性能,仅在视觉效果价值超过性能预算的核心特效中使用。
世界空间与局部空间:对于附着在移动Actor上的特效,使用局部空间模拟(粒子继承父组件的变换并随其移动)。对于发射后应保持静止的特效(如地面冲击 crater,粒子不应跟随移动Actor),使用世界空间模拟。空间设置在Niagara编辑器中每个发射器的属性里。
Related Skills
相关技能
- — component creation, attachment, lifecycle
ue-actor-component-architecture - — particle material setup, dynamic material instances
ue-materials-rendering - — UObject lifetime, delegates, UPROPERTY references
ue-cpp-foundations
- — 组件创建、附着、生命周期
ue-actor-component-architecture - — 粒子材质设置、动态材质实例
ue-materials-rendering - — UObject生命周期、委托、UPROPERTY引用
ue-cpp-foundations