Hi, I’m following this tutorial (link text) but I’m doing in in C++ instead of blueprint. The issue I have is that the Static Meshes of the Spline Mesh Components are not visible in the editor, unless I go in PIE mode.
My code is below. Note that if you to hit the REFRESH checkbox under the Details tab to update the component (also, make sure to select a static mesh!)
Thanks!
TrackData.h
#pragma once
#include "TrackData.generated.h"
USTRUCT(BlueprintType)
struct FTrackData
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, Category = "GuardRail")
bool LeftGuardRail = true;
UPROPERTY(EditAnywhere, Category = "GuardRail")
bool RightGuardRail = true;
UPROPERTY(EditAnywhere, Category = "Geometry")
float Width = 15.f;
UPROPERTY(EditAnywhere, Category = "Geometry")
float Thickness = 1.f;
UPROPERTY(EditAnywhere, Category = "Geometry")
float Bank = 0.f;
};
TrackGenerator.h
#pragma once
#include "GameFramework/Actor.h"
#include "TrackData.h"
#include "TrackGenerator.generated.h"
class USplineComponent;
class USplineMeshComponent;
//struct FTrackData;
UCLASS()
class RETRO_API ATrackGenerator : public AActor
{
GENERATED_BODY()
private:
UPROPERTY(EditAnywhere, Category = "_REFRESH")
bool REFRESH = false;
public:
// Sets default values for this actor's properties
ATrackGenerator();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
USceneComponent *SceneComponent = nullptr;
UPROPERTY(EditAnywhere, Category = "Spline")
USplineComponent *SplineComponent = nullptr;
UPROPERTY(VisibleAnywhere, Category = "Spline")
int8 NbSplinePoints;
UPROPERTY(EditAnywhere, Category = "TrackData")
TArray<FTrackData> TrackData;
private:
#if WITH_EDITOR
virtual void PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) override;
void DestroySplineComponents();
#endif
void BuildTrackElement(int Index, UStaticMesh *Mesh);
UPROPERTY(EditAnywhere, Category = "Spline")
UStaticMesh *StaticMesh = nullptr;
UPROPERTY(VisibleAnywhere, Category = "Spline")
TArray<USplineMeshComponent*> SplineMeshComponents;
};
TrackGenerator.cpp
#include "Retro.h"
#include "Runtime/Engine/Classes/Components/SplineComponent.h"
#include "Runtime/Engine/Classes/Components/SplineMeshComponent.h"
#include "TrackGenerator.h"
// Sets default values
ATrackGenerator::ATrackGenerator()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
SceneComponent = CreateDefaultSubobject<USceneComponent>(FName("SceneComponent"));
SceneComponent->SetMobility(EComponentMobility::Static);
SetRootComponent(SceneComponent);
SplineComponent = CreateDefaultSubobject<USplineComponent>(FName("SplineComponent"));
SplineComponent->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
SplineComponent->SetMobility(EComponentMobility::Static);
}
#if WITH_EDITOR
void ATrackGenerator::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
REFRESH = false;
UE_LOG(LogTemp, Warning, TEXT("PostEditChangeProperty"));
DestroySplineComponents();
NbSplinePoints = SplineComponent->GetNumberOfSplinePoints();
while (TrackData.Num() != NbSplinePoints)
{
if (TrackData.Num() < NbSplinePoints)
{
TrackData.Push(FTrackData());
}
else if (TrackData.Num() > NbSplinePoints)
{
TrackData.Pop();
}
}
if (StaticMesh)
{
for (int8 i = 0; i < NbSplinePoints - 1; i++)
{
UE_LOG(LogTemp, Warning, TEXT("Building: %i"), i);
BuildTrackElement(i, StaticMesh);
}
}
}
void ATrackGenerator::DestroySplineComponents()
{
int8 NbPointsToDestroy = SplineMeshComponents.Num();
for (int8 i = 0; i < NbPointsToDestroy; i++)
{
UE_LOG(LogTemp, Warning, TEXT("DELETING: %i"), i);
USplineMeshComponent *Component = SplineMeshComponents.Pop();
if (Component)
{
Component->DestroyComponent();
}
}
UE_LOG(LogTemp, Warning, TEXT("SplineMeshComponents.Num(): %i"), SplineMeshComponents.Num());
}
#endif
void ATrackGenerator::BuildTrackElement(int Index, UStaticMesh *Mesh)
{
// Store relevant data for each track element
FVector LocalLocationStart;
FVector LocalTangentStart;
SplineComponent->GetLocalLocationAndTangentAtSplinePoint(Index, LocalLocationStart, LocalTangentStart);
FVector LocalLocationEnd;
FVector LocalTangentEnd;
int NextIndex = (Index + 1) % NbSplinePoints;
SplineComponent->GetLocalLocationAndTangentAtSplinePoint(NextIndex, LocalLocationEnd, LocalTangentEnd);
// Use stored data to build track element
USplineMeshComponent *SplineMeshComponent = NewObject<USplineMeshComponent>(SplineComponent);
SplineMeshComponent->SetStaticMesh(Mesh);
SplineMeshComponent->SetStartAndEnd(LocalLocationStart, LocalTangentStart, LocalLocationEnd, LocalTangentEnd);
SplineMeshComponent->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
SplineMeshComponent->bVisible = true;
SplineMeshComponent->SetMobility(EComponentMobility::Static);
SplineMeshComponent->bSmoothInterpRollScale = true;
SplineMeshComponent->SetCollisionProfileName(FName("BlockAll"));
SplineMeshComponent->SetStartRoll(TrackData[Index].Bank);
SplineMeshComponent->SetStartScale(FVector2D(TrackData[Index].Width, TrackData[Index].Thickness));
SplineMeshComponent->SetEndRoll(TrackData[NextIndex].Bank);
SplineMeshComponent->SetEndScale(FVector2D(TrackData[NextIndex].Width, TrackData[NextIndex].Thickness));
SplineMeshComponents.Push(SplineMeshComponent);
UE_LOG(LogTemp, Warning, TEXT("Index:%i LocationStart:%s TangentStart:%s LocationEnd:%s TangentEnd:%s"), Index, *LocalLocationStart.ToString(), *LocalTangentStart.ToString(), *LocalLocationEnd.ToString(), *LocalTangentEnd.ToString());
}
// Called when the game starts or when spawned
void ATrackGenerator::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ATrackGenerator::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
}