Spline Circle Forward Vector Flips

Hi!

I’m trying to create a spline circle, and I have spline meshes attached to it. I noticed spline point 2 and 3 (the last two) have flipped forward vectors, which causes my spline meshes to flip also.

This is the code I’m using:

{
    if (!SplineComponent) return;
    SplineComponent->ClearSplinePoints(false);

    // Add spline points in a circle, constrained to Y and Z axes
    const int32 NumberOfPoints = 4;
    for (int32 i = 0; i < NumberOfPoints; ++i)
    {
        float Angle = (i / static_cast<float>(NumberOfPoints)) * 2.0f * PI;
        float Y = SplineRadius * FMath::Cos(Angle);
        float Z = SplineRadius * FMath::Sin(Angle);
        float X = 0.0f;  // X remains constant in my use case

        FVector Position(X, Y, Z);
        FVector Tangent = FVector(0.0f, -SplineRadius * FMath::Sin(Angle), SplineRadius * FMath::Cos(Angle));
        SplineComponent->AddSplinePoint(Position, ESplineCoordinateSpace::Local, false);
        SplineComponent->SetTangentAtSplinePoint(i, Tangent, ESplineCoordinateSpace::Local, false);
    }
    SplineComponent->UpdateSpline();
}

Anybody have some ideas how I can fix the forward vector flips? I haven’t across any solutions online, and changing the up vector also had no effect.

Here is a screenshot of said flipping on the spline meshes.

Not sure if this is allowed, but I have to bump this, because this is affecting my progress in my game soo much. Any help would be GREATLY appreciated! You can also DM me if you want to do it paid!

I tried another pass where I increased the amount of spline point, but it still results in flipped forward vectors.

void UCannonSplineComponent::CreateSplineMeshesWithOffset()
{
    const int32 NumSplinePoints = GetNumberOfSplinePoints();

    if (IsValid(SplineMesh))
    {
        for (int32 i = 0; i < NumSplinePoints - 1; i++)
        {
            USplineMeshComponent* SplineMeshComponent = NewObject<USplineMeshComponent>(this);
            SplineMeshComponent->SetMobility(EComponentMobility::Movable);

            FVector StartPos, StartTangent, EndPos, EndTangent;
            GetLocationAndTangentAtSplinePoint(i, StartPos, StartTangent, ESplineCoordinateSpace::Local);
            GetLocationAndTangentAtSplinePoint(i + 1, EndPos, EndTangent, ESplineCoordinateSpace::Local);

            // Apply the forward X offset only to the X component of the start and end positions
            StartPos.X += ForwardOffset;
            EndPos.X += ForwardOffset;

            // Set the static mesh for this spline mesh component
            SplineMeshComponent->SetStaticMesh(SplineMesh);

            // Set the start and end positions for the spline mesh with the applied X offset
            SplineMeshComponent->SetStartAndEnd(StartPos, StartTangent, EndPos, EndTangent);

            SplineMeshComponent->SetForwardAxis(SplineForwardAxis, true);

            if (SplineUpDirections.IsValidIndex(i))
            {
                SplineMeshComponent->SetSplineUpDir(SplineUpDirections[i], true);
            }
            else
            {
                SplineMeshComponent->SetSplineUpDir(SplineUpDir, true);
            }

            SplineMeshComponent->RegisterComponent();
            SplineMeshComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);

            // Add the spline mesh to the array for later reference
            SplineMeshes.Add(SplineMeshComponent);
        }

        // Handle the case for a closed loop by connecting the last point to the first point
        if (IsClosedLoop())
        {
            USplineMeshComponent* SplineMeshComponent = NewObject<USplineMeshComponent>(this);
            SplineMeshComponent->SetMobility(EComponentMobility::Movable);

            FVector StartPos, StartTangent, EndPos, EndTangent;
            // Last spline point
            GetLocationAndTangentAtSplinePoint(NumSplinePoints - 1, StartPos, StartTangent, ESplineCoordinateSpace::Local);
            // First spline point (closing the loop)
            GetLocationAndTangentAtSplinePoint(0, EndPos, EndTangent, ESplineCoordinateSpace::Local);

            // Apply the forward X offset only to the X component of the start and end positions
            StartPos.X += ForwardOffset;
            EndPos.X += ForwardOffset;

            // Set the static mesh for this spline mesh component
            SplineMeshComponent->SetStaticMesh(SplineMesh);

            // Set the start and end positions for the spline mesh with the applied X offset
            SplineMeshComponent->SetStartAndEnd(StartPos, StartTangent, EndPos, EndTangent);

            if (SplineUpDirections.IsValidIndex(NumSplinePoints - 1))
            {
                SplineMeshComponent->SetSplineUpDir(SplineUpDirections[NumSplinePoints - 1], true);
            }
            else
            {
                SplineMeshComponent->SetSplineUpDir(SplineUpDir, true);
            }

            SplineMeshComponent->RegisterComponent();
            SplineMeshComponent->AttachToComponent(this, FAttachmentTransformRules::KeepRelativeTransform);

            // Add the spline mesh to the array for later reference
            SplineMeshes.Add(SplineMeshComponent);
        }
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("SplineMesh is not set! Cannot create spline meshes."));
    }
}

void UCannonSplineComponent::CreateCircleSplinePoints(USplineComponent* SplineComponent, float SplineRadius)
{
    if (!SplineComponent) return;

    // Clear existing spline points
    SplineComponent->ClearSplinePoints(false);

    // Add spline points in a circle, constrained to Y and Z axes, with tangents for a perfect circle
    const int32 NumberOfPoints = 16; // Equivalent to the 16 points for a proper circle
    const float AngleIncrement = 2.0f * PI / NumberOfPoints;

    for (int32 i = 0; i < NumberOfPoints; ++i)
    {
        float Angle = i * AngleIncrement;
        float Y = SplineRadius * FMath::Cos(Angle);
        float Z = SplineRadius * FMath::Sin(Angle);
        float X = 0.0f;  // X remains constant

        FVector Position(X, Y, Z);
        FVector Tangent = FVector(0.0f, -SplineRadius * FMath::Sin(Angle), SplineRadius * FMath::Cos(Angle));
        SplineComponent->AddSplinePoint(Position, ESplineCoordinateSpace::Local, true);
        SplineComponent->SetTangentAtSplinePoint(i, Tangent, ESplineCoordinateSpace::Local, true);
    }

    SplineComponent->UpdateSpline();
}