VFX¶
Niagara everywhere. Two patterns dominate: the multi-anchor fire spawn, and the expanding ring. Both are reusable.
Niagara as the default¶
Module dependency Niagara is in Rephrased_Demo.Build.cs. New particle work uses Niagara unless we have a specific reason to fall back to Cascade (UParticleSystem is still referenced on ARune::DiscoveryVFXTemplate for legacy assets, but ARune::DiscoveryNiagaraVFX is the preferred path).
Fire and the multi-anchor pattern¶
The most reusable VFX trick in this project. A single UFireComponent can spawn multiple Niagara instances at designer-placed anchor points.
Anchor: UFireSpawnPointComponent is a USceneComponent subclass with no logic. Designers add one or more to a burnable actor and position them in the editor (FireTrunkBase, FireCrownLeft, FireBranchTip, etc.).
Spawn: when UFireComponent::SpawnFireParticles runs, it iterates every UFireSpawnPointComponent on the owner and instantiates FireParticleSystem at each one's relative transform — including scale. The spawn point's transform controls where each flame sits and how big it is.
Tracking: the live UNiagaraComponents are stored in UFireComponent::FireParticleComponents (a TArray<TObjectPtr<UNiagaraComponent>>) so the extinguish path can tear them all down on OnEffectRemoved.
Fallback: if the actor has zero UFireSpawnPointComponents, UFireComponent spawns one instance at the actor root. Legacy actors keep working without changes.
Blueprint override: Blueprint subclasses overriding SpawnFireParticles must populate FireParticleComponents themselves so the native extinguish path can clean up. The base C++ implementation is the easy default; override only when you need to do something more specific (e.g. spawn a different system per anchor type).
This pattern is reusable for any "swarm of effects on a single actor" — birds taking off, water dripping, smoke trails. New components should mimic the shape: a typed marker scene component + a tracker array on the driving component.
Expanding ring¶
AExpandingRingActor is the visual for range-based sentence activation. Detailed in Rune actors and placement. Two visual responsibilities:
- Mesh scaling. A
UStaticMeshComponent(RingVisual) attached to aRingRoot. The actor measures the mesh's bounding box atBeginPlay(MeshBaseRadius,MaxMeshScale) and scales it from 0 to fillMaxRadiusoverDuration. Subclass in Blueprint and swap the mesh — the math handles arbitrary mesh shapes. - Detection sphere. A separate per-tick sphere check at the matching radius. The visual and the detection are decoupled so the mesh can be a stylized shape (a glowing shockwave, a runic disc) without breaking gameplay timing.
Recommended Niagara use: attach a Niagara emitter to RingVisual for the leading-edge effect, drive the emitter's spawn rate from the actor's elapsed time (read via Blueprint or a Niagara user parameter).
Discovery VFX¶
Per-rune. URuneDataAsset doesn't carry the discovery VFX directly — ARune does. Two slots:
DiscoveryVFXTemplate(UParticleSystem) — Cascade, legacy.DiscoveryNiagaraVFX(UNiagaraSystem) — Niagara, preferred.
PlayDiscoveryVFX picks whichever is set. New rune visuals should fill DiscoveryNiagaraVFX only.
Slot ring¶
ARuneSlot::SlotRingEffect is a UNiagaraComponent that activates when the slot participates in a valid sentence. The calculator's SlotActivationMode (Sequential vs Simultaneous) controls timing. Simultaneous fires every slot's ring at once; Sequential fires them one by one with SequentialSlotDelay in between, then starts the expanding ring.
Tech artists configure the Niagara system asset on the slot's BP. The component is auto-attached and auto-cleaned by ARuneSlot.
Performance notes¶
- Niagara components on transient actors (rune pickups, expanding rings) are GC'd along with the owning actor — no manual cleanup needed.
- Components attached to long-lived actors (slot rings, fire on a campfire) need explicit teardown if the effect should stop.
UFireComponent::OnEffectRemovedis the model: walkFireParticleComponents, deactivate, destroy. - Per-tick sphere checks (the expanding ring) are cheap at puzzle scale but quadratic in pathological cases. If a level has hundreds of runeable objects in the same range, profile.
Tech artist hand-off¶
| Asset | What the artist owns | What engineering owns |
|---|---|---|
NS_Fire |
the visual look, lifetime, color ramps, smoke layer | spawn anchor placement and intensity scaling are designer/engineering |
NS_RingExpanding |
the visual look, the leading-edge feel | the actor's expansion math and target detection |
NS_Discovery |
the per-rune flair | the trigger conditions inside ARune |
NS_SlotRing |
the slot activation visual | when it fires (Simultaneous vs Sequential) |
Names above are conventions; check the actual asset paths in the Content browser.
Source files¶
Source/Rephrased_Demo/RuneSystem/Components/FireComponent.hSource/Rephrased_Demo/RuneSystem/Components/FireSpawnPointComponent.hSource/Rephrased_Demo/ExpandingRingActor.hSource/Rephrased_Demo/Rune.hSource/Rephrased_Demo/RuneSlot.h
Known constraints¶
- Cascade (
UParticleSystem) is technically still supported but not maintained. Migrate any remainingDiscoveryVFXTemplatereferences to Niagara when the asset is touched. - The Niagara system on
BP_FireComponentis currently a single asset for every burnable actor. Per-noun flame variation (campfire vs torch vs forest fire) is on the polish list — easy to add viaVerbComponentOverridesonce we author the variants. - No GPU particle budget enforcement. If the team starts shipping levels with dozens of simultaneous fires, add an LOD path on
UFireComponent.
Change history¶
- 2026-05-08 — Gabriel Li — Added baseline change history section for weekly wiki maintenance.