So I had a simple blueprint to produce rails, pipes, etc using a Spline and SplineMeshComponents which I decided to convert to C++ (mainly to help me learn UE4 C++), anyway it has not been going well and after three days working on it I’m close to giving up, The main issue I have is the mesh keeps vanishing every time I manipulate the spline, I’ve tried recreating it on every OnConstruction event call but for whatever reason nothing seems to work, one idea I have is it might not be destroying the previous SplineMeshComponent objects correctly since the object name increments each time it’s called, but at this point I have no clear solution.
Header
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include <Components/SplineComponent.h>
#include <Components/SplineMeshComponent.h>
#include <Components/SceneComponent.h>
#include <Materials/MaterialInstance.h>
#include <Components/StaticMeshComponent.h>
#include <Engine/StaticMesh.h>
#include <Containers/Array.h>
#include "Chryseus_SplineMesh.generated.h"
UCLASS()
class GAME_API AChryseus_SplineMesh : public AActor
{
GENERATED_BODY()
public:
AChryseus_SplineMesh();
protected:
virtual void BeginPlay() override;
virtual void OnConstruction(const FTransform &Transform) override;
virtual void PostEditChangeProperty(struct FPropertyChangedEvent &PropertyChangedEvent) override;
virtual void PostEditMove(bool bFinished) override;
public:
virtual void Tick(float DeltaTime) override;
void createSplineMesh();
void updateSplineMesh();
USceneComponent* C_Scene;
UPROPERTY()
USplineComponent* C_Spline;
UPROPERTY()
TArray<USplineMeshComponent *> A_SplineMesh;
UPROPERTY(EditAnywhere, Category = Mesh)
UStaticMesh *SingleMesh;
UPROPERTY(EditAnywhere, Category = Material)
UMaterial *Material;
bool *created;
int32 *numPoints;
};
Source
#include "Chryseus_SplineMesh.h"
// Sets default values
AChryseus_SplineMesh::AChryseus_SplineMesh()
{
UE_LOG(LogTemp, Display, TEXT("AChryseus_SplineMesh Constructor."));
UStaticMesh *useMesh = nullptr;
// Create subobjects
C_Spline = CreateDefaultSubobject<USplineComponent>(TEXT("Spline"));
C_Scene = CreateDefaultSubobject<USceneComponent>(TEXT("SceneRoot"));
//SingleMesh = CreateDefaultSubobject<UStaticMesh>(TEXT("Single Mesh"));
Material = CreateDefaultSubobject<UMaterial>(TEXT("Override Material"));
// Set root component
RootComponent = C_Scene;
// Attach spline to root
C_Spline->SetupAttachment(RootComponent);
// Set Mobility
RootComponent->SetMobility(EComponentMobility::Type::Movable);
C_Spline->SetMobility(EComponentMobility::Type::Movable);
//Setup USplineMeshComponent array
A_SplineMesh.Empty();
created = new bool;
*created = false;
numPoints = new int32;
// Enable ticking
PrimaryActorTick.bCanEverTick = true;
}
void AChryseus_SplineMesh::OnConstruction(const FTransform& Transform)
{
if (!created)
{
this->createSplineMesh();
}
else
{
UE_LOG(LogTemp, Display, TEXT("Updating"));
this->updateSplineMesh();
}
}
void AChryseus_SplineMesh::createSplineMesh()
{
FVector locStart;
FVector tanStart;
FVector locEnd;
FVector tanEnd;
// Clean up stale mesh components
if (A_SplineMesh.Num() > 0)
{
for (int32 i = 0; i < A_SplineMesh.Num(); i++)
{
if (A_SplineMesh*)
{
A_SplineMesh*->DetachFromParent();
A_SplineMesh*->DestroyComponent();
}
}
A_SplineMesh.Empty();
}
*numPoints = C_Spline->GetNumberOfSplinePoints(); // Store number of spline points
for (int32 i = 0; i < C_Spline->GetNumberOfSplinePoints() -1; i++)
{
USplineMeshComponent *splineMesh = NewObject<USplineMeshComponent>(this);
UE_LOG(LogTemp, Display, TEXT("Spline Points: %d"), *numPoints);
UE_LOG(LogTemp, Display, TEXT("Added new USplineMeshComponent: %s"), *splineMesh->GetName());
splineMesh->CreationMethod = EComponentCreationMethod::UserConstructionScript;
C_Spline->GetLocationAndTangentAtSplinePoint(i, locStart, tanStart, ESplineCoordinateSpace::Local);
C_Spline->GetLocationAndTangentAtSplinePoint(i+1, locEnd, tanEnd, ESplineCoordinateSpace::Local);
UE_LOG(LogTemp, Display, TEXT("Start location and tangent: %s / %s"), *locStart.ToCompactString(), *tanStart.ToCompactString());
UE_LOG(LogTemp, Display, TEXT("End location and tangent: %s / %s"), *locEnd.ToCompactString(), *tanEnd.ToCompactString());
splineMesh->SetMobility(EComponentMobility::Type::Movable);
splineMesh->SetForwardAxis(ESplineMeshAxis::X);
splineMesh->SetStaticMesh(SingleMesh);
splineMesh->SetMaterial(0, Material);
splineMesh->SetStartAndEnd(locStart, tanStart, locEnd, tanEnd);
splineMesh->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
splineMesh->AttachToComponent(C_Spline, FAttachmentTransformRules::FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
A_SplineMesh.Add(splineMesh);
}
*created = true;
}
void AChryseus_SplineMesh::updateSplineMesh()
{
// Try recreate the mesh instead of updating
*created = false;
createSplineMesh();
return;
FVector locStart;
FVector tanStart;
FVector locEnd;
FVector tanEnd;
if (C_Spline->GetNumberOfSplinePoints() != *numPoints)
{
// Spline points have been changed
UE_LOG(LogTemp, Display, TEXT("Recreating spline mesh"));
*created = false;
createSplineMesh();
return;
}
else
{
for (int32 i = 0; i < A_SplineMesh.Num(); i++)
{
// Update spline meshes
if (!A_SplineMesh*) { UE_LOG(LogTemp, Display, TEXT("Null spline mesh!")); *created = false; createSplineMesh(); return; }
C_Spline->GetLocationAndTangentAtSplinePoint(i, locStart, tanStart, ESplineCoordinateSpace::Local);
C_Spline->GetLocationAndTangentAtSplinePoint(i+1, locEnd, tanEnd, ESplineCoordinateSpace::Local);
UE_LOG(LogTemp, Display, TEXT("Start location and tangent: %s / %s"), *locStart.ToCompactString(), *tanStart.ToCompactString());
UE_LOG(LogTemp, Display, TEXT("End location and tangent: %s / %s"), *locEnd.ToCompactString(), *tanEnd.ToCompactString());
A_SplineMesh*->SetMobility(EComponentMobility::Type::Movable);
A_SplineMesh*->SetForwardAxis(ESplineMeshAxis::X);
UE_LOG(LogTemp, Display, TEXT("Static mesh is %s"), *SingleMesh->GetPathName());
A_SplineMesh*->SetStaticMesh(SingleMesh);
A_SplineMesh*->SetMaterial(0, Material);
A_SplineMesh*->SetStartAndEnd(locStart, tanStart, locEnd, tanEnd);
A_SplineMesh*->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
}
}
void AChryseus_SplineMesh::PostEditChangeProperty(struct FPropertyChangedEvent &PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
}
void AChryseus_SplineMesh::PostEditMove(bool bFinished)
{
Super::PostEditMove(bFinished);
}
void AChryseus_SplineMesh::BeginPlay()
{
Super::BeginPlay();
}
void AChryseus_SplineMesh::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}