Issue drawing line with Niagara System

Hi everyone,

I’m trying to draw a line using an array of FVector data points, and while the debug lines display correctly (as shown in the yellow and purple lines in the images), the Niagara system doesn’t display the path as expected. It displays correctly at the beginning for a very short time but then either doesn’t display or shows the giant swirls which seems to have tons of lines that loop back.

I have confirmed that the FVector data is correctly passed to the Niagara system, by debugging the assigned values.

Issue:

  • The FVector data is shown correctly via debug lines, but when assigning it to the Niagara system, it doesn’t render the path correctly.
  • I’ve confirmed the vector data is correct by printing it to the log.

Setup:

  • I am using two Niagara components (ReachableNiagaraComponent and UnreachableNiagaraComponent), and I’m trying to pass an array of FVector data to the ReachableNiagaraComponent via the SetNiagaraArrayVector function.

Here’s the relevant code:

APlayerGizmos::APlayerGizmos(): ReachableNiagaraSystem(nullptr), UnreachableNiagaraSystem(nullptr), BaseDecalScale(),
                                ReachableNiagaraComponent(nullptr),
                                UnreachableNiagaraComponent(nullptr)
{
    // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    // Create Niagara components as subobjects
    ReachableNiagaraComponent = CreateDefaultSubobject<UNiagaraComponent>(TEXT("ReachableNiagaraComponent"));
    ReachableNiagaraComponent->SetupAttachment(RootComponent);
    ReachableNiagaraComponent->SetAutoActivate(false);
    ReachableNiagaraComponent->Deactivate();

    UnreachableNiagaraComponent = CreateDefaultSubobject<UNiagaraComponent>(TEXT("UnreachableNiagaraComponent"));
    UnreachableNiagaraComponent->SetupAttachment(RootComponent);
    UnreachableNiagaraComponent->SetAutoActivate(false);
    UnreachableNiagaraComponent->Deactivate();
}

void APlayerGizmos::DrawReachablePath(const TArray<FVector>& InNavPath, float InDistanceAgentAbleToTravel)
{
    // Calculate the reachable path points
    TArray<FVector> ReachablePathPoints;
    bool bReachedMaxDistance = false;
    CalculateReachablePathPoints(InNavPath, InDistanceAgentAbleToTravel, ReachablePathPoints, bReachedMaxDistance);

    // Debug length of reachable path
    UE_LOG(LogTemp, Log, TEXT("PLAYER GIZMOS: Calculated Reachable Path Length: %d"), ReachablePathPoints.Num());

    // Draw a debug path for reachable path
    for (int32 i = 0; i < ReachablePathPoints.Num() - 1; ++i)
    {
        DrawDebugLine(
            GetWorld(),
            ReachablePathPoints[i] + FVector(0.0f, 0.0f, 10.0f),
            ReachablePathPoints[i + 1] + FVector(0.0f, 0.0f, 10.0f),
            FColor::Yellow,
            false,
            1.5f,
            0,
            2.0f
        );

        UE_LOG(LogTemp, Log, TEXT("PLAYER GIZMOS: Reachable Path Point: %s"), *ReachablePathPoints[i].ToString());
    }

    // Draw a debug path for the entire path
    for (int32 i = 0; i < InNavPath.Num() - 1; ++i)
    {
        DrawDebugLine(
            GetWorld(),
            InNavPath[i] + FVector(0.0f, 0.0f, 50.0f),
            InNavPath[i + 1] + FVector(0.0f, 0.0f, 50.0f),
            FColor::Purple,
            false,
            1.5f,
            0,
            2.0f
        );
    }

    // Debug the current 'TargetPath' Data Interface
    TArray<FVector> DebugTargetPath =
        UNiagaraDataInterfaceArrayFunctionLibrary::GetNiagaraArrayVector(
            ReachableNiagaraComponent,
            FName("TargetPath")
        );

    for (int32 i = 0; i < DebugTargetPath.Num(); ++i)
    {
        UE_LOG(LogTemp, Log, TEXT("PLAYER GIZMOS: Debug Target Path Point: %s"), *DebugTargetPath[i].ToString());
    }

    // Access the 'TargetPath' Data Interface
    UNiagaraDataInterfaceArrayFunctionLibrary::SetNiagaraArrayVector(
        ReachableNiagaraComponent,
        FName("TargetPath"),
        ReachablePathPoints
    );

    // Activate the Niagara component after setting the data
    ReachableNiagaraComponent->Activate(true);
}

Has anyone experienced something similar with Niagara systems or encountered rendering issues when passing FVector arrays? Any suggestions on what could be causing this discrepancy?

Thanks!





the swirls are the ribbon renderer connecting points in the wrong order, plus the array being resampled every frame. the fix is a one time ordered burst:

  1. single spawn burst: set emitter to spawn burst instant with count equal to your array size, then stop the emitter from respawning. right now the system is likely emitting continuously, so particles grab random or shifting indices and the line walks itself into loops.
  2. sample by index in particle spawn: in particle spawn, read your vector array with the particle index as the index (index equals particles spawned or use particles id modulo size) and write it to position. every particle gets exactly one point.
  3. ordering for the ribbon: the ribbon renderer connects particles in their id order, so the array must be sorted along the path before it reaches the system. if your debug lines are fine but the ribbon loops, the array order itself is shuffled relative to draw order.
  4. normalized age drives the shape: write the array index divided by array size into normalized age (or a custom float) in particle spawn, then drive ribbon u, width and color from it. this makes the ribbon respect path order instead of particle lifetime.
  5. space: make sure positions match the system transform, set local space on the emitter if the array is local, keep world space if the points are world.
  6. rewrite the array only when the path changes, not every tick. since blueprint cannot write vector arrays into niagara at all (no set array node in stock), the c++ route is SetNiagaraArrayVector on the component, or the Expose Niagara Variables C++ & Blueprints plugin which exposes the array types plus 18 variable types across all 8 namespaces with getters, in blueprint and c++, turning the whole feed into one node: Expose Niagara Variables C++ & Blueprints | Fab