C++ Spline creates hundreds of meshes

Hello, I’m creating a spline through C++ but changing the spline only adds extra meshes:

Here is the code I used:



void ASpline::OnConstruction(const FTransform& Transform){
	SetupSpline();
}
void ASpline::PostEditMove(bool bFinished) {
	Super::PostEditMove(bFinished);
	SetupSpline();
}
void ASpline::SetupSpline(){

	for (int32 i = 0; i < Spline->GetNumSplinePoints() - 1; i++)
	{
		USplineMeshComponent* SplineMesh = ConstructObject<USplineMeshComponent>(USplineMeshComponent::StaticClass(), this);

		SplineMesh->CreationMethod = EComponentCreationMethod::UserConstructionScript;
		SplineMesh->SetMobility(EComponentMobility::Movable);
		SplineMesh->AttachParent = Spline;

		SplineMesh->bCastDynamicShadow = false;
		SplineMesh->SetStaticMesh(Mesh);

		FVector pointLocationStart, pointTangentStart, pointLocationEnd, pointTangentEnd;
		Spline->GetLocalLocationAndTangentAtSplinePoint(i, pointLocationStart, pointTangentStart);
		Spline->GetLocalLocationAndTangentAtSplinePoint(i + 1, pointLocationEnd, pointTangentEnd);

		SplineMesh->SetStartAndEnd(pointLocationStart, pointTangentStart, pointLocationEnd, pointTangentEnd);
	}
	RegisterAllComponents();
}

Edit:
It properly removes the old meshes when changing any property in the editor (mesh, hiddenInGame, etc), just not when the points are moved.
How can I make that apply to moving the points as well?

Thanks!

That is because you ARE creating (ConstructObject) new SplineMeshComponents every time SetupSpline is called. Normally with Construction Script for Blueprint it would first remove all components that were added previously and then add the new components from executing the Construction Script again.

I don’t know what you need to do in order to trigger this to happen from C++ but here’s a fairly simple alternative solution (not compiled or tested!):
Add this to your ASpline class in the .h


UPROPERTY()
TArray<class USplineMeshComponent*> AddedSplineMeshComponents;

And in the .cpp


ASpline::ASpline()
	: Super()
{
	AddedSplineMeshComponents.Empty();
}

void ASpline::SetupSpline()
{
	int32 SplineMeshComponentsNum = AddedSplineMeshComponents.Num();

	//remove spline mesh components to get to Spline->GetNumSplinePoints() - 1
	for (int32 Index = SplineMeshComponentsNum  - 1; Index >= Spline->GetNumSplinePoints() - 1; --Index )
	{
		AddedSplineMeshComponents[Index].DetachFromParent();
		AddedSplineMeshComponents[Index].UnregisterComponent();
	}

	//remove entries from array or make room for new components
	AddedSplineMeshComponents.SetNum(Spline->GetNumSplinePoints() - 1, true);

	//construct spline mesh components to get to Spline->GetNumSplinePoints() - 1
	//previously constructed components will remain in the AddedSplineMeshComponents array unchanged
	for (int32 Index = SplineMeshComponentsNum; Index < Spline->GetNumSplinePoints() - 1; ++Index)
	{
		AddedSplineMeshComponents[Index] = ConstructObject<USplineMeshComponent>(USplineMeshComponent::StaticClass(), this);
	}

	//update components
	for (int32 Index = 0; Index < AddedSplineMeshComponents.Num(); ++Index )
	{
		AddedSplineMeshComponents[Index]->CreationMethod = EComponentCreationMethod::UserConstructionScript;
		AddedSplineMeshComponents[Index]->SetMobility(EComponentMobility::Movable);
		AddedSplineMeshComponents[Index]->AttachParent = Spline;

		AddedSplineMeshComponents[Index]->bCastDynamicShadow = false;
		AddedSplineMeshComponents[Index]->SetStaticMesh(Mesh);

		FVector pointLocationStart, pointTangentStart, pointLocationEnd, pointTangentEnd;
		Spline->GetLocalLocationAndTangentAtSplinePoint(i, pointLocationStart, pointTangentStart);
		Spline->GetLocalLocationAndTangentAtSplinePoint(i + 1, pointLocationEnd, pointTangentEnd);

		AddedSplineMeshComponents[Index]->SetStartAndEnd(pointLocationStart, pointTangentStart, pointLocationEnd, pointTangentEnd);
	}
	RegisterAllComponents();
}

EDIT:
Thanks, but still problems

My modified code:



ASpline::ASpline()
{
	AddedSplineMeshes.Empty();
}

void ASpline::SetupSpline() {

	//Remove existing meshes
	for (int32 i = 0; i < AddedSplineMeshes.Num(); i++){
		AddedSplineMeshes*->DetachFromParent();
		AddedSplineMeshes*->UnregisterComponent();
	}
	AddedSplineMeshes.Empty();


	for (int32 i = 0; i < Spline->GetNumberOfSplinePoints() - 1; i++)
	{
		USplineMeshComponent* tempMesh = ConstructObject<USplineMeshComponent>(USplineMeshComponent::StaticClass(), this);
		AddedSplineMeshes.Add(tempMesh);

		tempMesh->CreationMethod = EComponentCreationMethod::UserConstructionScript;
			
		tempMesh->SetMobility(EComponentMobility::Static);
		tempMesh->AttachParent = Spline;
			
		tempMesh->SetStaticMesh(SplineMesh);
		
		// Code to set curves

		tempMesh->RegisterComponent();
	}
}


The entire thing disappears when the game runs, no matter what I do.

If I check ‘Hidden in game’, then the shadows immediately disappear in editor, so I know that isn’t the problem since it’s easy to see if that is turned on/off.

Edit2: I didn’t have UPROPERTY() for my array. Adding it made them appear in game too. Thanks!

Thank you for this code snippet. It is precise and deals with adding meshes and cleaning them up. Well done.

How to do this correctly

This took a while to sort out. I still have actor cleanup issues, but a teleport arc spline is working in c++

I hope this helps someone else trying to implement VR teleport arc in C++.



... H CODE

UPROPERTY()
TArray<class USplineMeshComponent*> AddedSplineMeshComponents;

UPROPERTY()
USplineComponent* TeleportArcSplineComponent;

... CPP CODE

for (int ArcIndex = 0; ArcIndex < OutPathPositions.Num() - 1; ArcIndex++) {

	USplineMeshComponent* ArcSplineMeshComponent = NewObject<USplineMeshComponent>();
	ArcSplineMeshComponent->CreationMethod = EComponentCreationMethod::UserConstructionScript;
	ArcSplineMeshComponent->SetMobility(EComponentMobility::Movable);
	ArcSplineMeshComponent->SetupAttachment(TeleportArcSplineComponent);
	ArcSplineMeshComponent->bCastDynamicShadow = false;

	FVector pointLocationStart, pointTangentStart, pointLocationEnd, pointTangentEnd;
	TeleportArcSplineComponent->GetLocalLocationAndTangentAtSplinePoint(ArcIndex, pointLocationStart, pointTangentStart);
	TeleportArcSplineComponent->GetLocalLocationAndTangentAtSplinePoint(ArcIndex + 1, pointLocationEnd, pointTangentEnd);

	// AGeneratedActor is my actor. It has a variable that holds the mesh: GeneratedActorMesh 
	FTransform SpawnTransform = FTransform(FRotator(0.0f, 0.0f, 0.0f), pointLocationStart, FVector(10.1f, 10.1f, 10.1f));
	AGeneratedActor* ArcSegment = GetWorld()->SpawnActor<AGeneratedActor>(AGeneratedActor::StaticClass(), SpawnTransform);
	ArcSplineMeshComponent->SetStaticMesh(ArcSegment->GeneratedActorMesh->GetStaticMesh());
	ArcSplineMeshComponent->SetMaterial(0, ArcSegment->GeneratedActorMesh->GetMaterial(0));

	ArcSplineMeshComponent->SetStartAndEnd(pointLocationStart, pointTangentStart, pointLocationEnd, pointTangentEnd);
	ArcSplineMeshComponent->RegisterComponentWithWorld(GetWorld());

	AddedSplineMeshComponents.Add(ArcSplineMeshComponent);

}