USceneComponents created at runtime are invisible

I’m new to UE4 and have stumbled across something related to CDOs that I don’t understand: my task was to have a Component (UIounTorchComponent) orbitting an Actor (relationship established via constructors) and then at runtime add several instances of a different Component (UMicroMeteorComponent) that orbit the UIounTorchComponent. I wasn’t seeing any of the meteors appear, so I simplified things for debug purposes to the point where the meteors should appear with increasing offsets (increase performed in UMicroMeteor::TickComponent()) from a known point in world space. Also for simplicity and to remove as many variables from the equation as possible, I had the Actor create the meteor Components directly.

runtime creation code:

float totalTime = 0.0f;
int meteorCount = 0;

// Called every frame
void AMollyPawn::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	totalTime += DeltaTime;
	if (totalTime >= 3 && meteorCount < 3)
	{
		auto* pMeteor = NewObject<UMicroMeteorComponent>(this, UMicroMeteorComponent::StaticClass());
		pMeteor->SetupAttachment(RootComponent);
		pMeteor->fSpawnPosMod = meteorCount * 100;
		pMeteor->RegisterComponent();
		meteorCount++;
		totalTime = 0.0f;
	}
...
}

meteor ctor, BeginPlay, and TickComponent code:

// Sets default values for this component's properties
UMicroMeteorComponent::UMicroMeteorComponent() 
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	/* with this code in the ctor, I only ever see one instance
	// Sphere shape will serve as our root component
	USphereComponent* SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootSphereComponent"));
	SphereComponent->InitSphereRadius(10.0f);
	SphereComponent->SetCollisionProfileName(TEXT("MicroMeteorPresence"));
	SphereComponent->SetupAttachment(this);
	// Create and position a mesh component so we can see where our spherical Molly is
	UStaticMeshComponent* SphereVisual = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("TheVisibleMolly"));
	SphereVisual->SetupAttachment(SphereComponent);
	static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereVisualAsset(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
	if (SphereVisualAsset.Succeeded())
	{
		SphereVisual->SetStaticMesh(SphereVisualAsset.Object);
		SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
		// Our sphere component has a radius of 10 units and the startercontent sphere mesh is 50, so scale it down by 80%
		SphereVisual->SetWorldScale3D(FVector(0.2f));
	}
	// Create a particle system that we can activate or deactivate
	MeteorParticles = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("MeteorFuryParticles"));
	MeteorParticles->SetupAttachment(SphereVisual);
	MeteorParticles->bAutoActivate = false;
	// visibility offset
	MeteorParticles->SetRelativeLocation(FVector(-20.0f, 0.0f, 20.0f));
	static ConstructorHelpers::FObjectFinder<UParticleSystem> ParticleAsset(TEXT("/Game/StarterContent/Particles/P_Fire.P_Fire"));
	if (ParticleAsset.Succeeded())
	{
		MeteorParticles->SetTemplate(ParticleAsset.Object);
	}
	// todo: trying simple world space positioning for debug
	UE_LOG(LogTemp, Warning, TEXT("MicroMeteor::ctor; setting world pos to about our maze hallway floor"));
	this->SetWorldLocation(FVector(-20.0f, 450.0f, 250.0f));
	*/
}


// Called when the game starts
void UMicroMeteorComponent::BeginPlay()
{
	Super::BeginPlay();
        // todo: the ctor code converted for use at runtime works, but what of it needs to be here?  Why did any of it have to be here?
	// Sphere shape will serve as our root component
	USphereComponent* SphereComponent = NewObject<USphereComponent>(this);
	SphereComponent->InitSphereRadius(10.0f);
	SphereComponent->SetCollisionProfileName(TEXT("MicroMeteorPresence"));
	SphereComponent->SetupAttachment(this);
	SphereComponent->RegisterComponent();
	// Create and position a mesh component so we can see where our spherical Molly is
	UStaticMeshComponent* SphereVisual = NewObject<UStaticMeshComponent>(this);
	SphereVisual->SetupAttachment(SphereComponent);
	UStaticMesh* SphereVisualAsset = LoadObject<UStaticMesh>(GetOuter(), TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere'"));
	SphereVisual->SetStaticMesh(SphereVisualAsset);
	SphereVisual->SetRelativeLocation(FVector(0.0f, 0.0f, -40.0f));
	// Our sphere component has a radius of 10 units and the startercontent sphere mesh is 50, so scale it down by 80%
	SphereVisual->SetWorldScale3D(FVector(0.2f));
	SphereVisual->RegisterComponent();
	
	// todo: trying simple world space positioning for debug
	UE_LOG(LogTemp, Warning, TEXT("MicroMeteor::ctor; setting world pos to about our maze hallway floor"));
	this->SetWorldLocation(FVector(-20.0f, 450.0f, 250.0f));
	
}


// Called every frame
void UMicroMeteorComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

        // offset pos slightly so we can clearly see different instances
	this->SetWorldLocation(FVector(-20.0f + fSpawnPosMod, 450.0f, 250.0f));
	UE_LOG(LogTemp, Warning, TEXT("MicroMeteor::ctor; resetting world pos to about our maze hallway floor plus %f, giving us location of %s"), fSpawnPosMod, *this->GetComponentLocation().ToString());
}

This resulted in a single instance of UMicroMeteorComponent becoming visible at the first offset and then nothing more. Logs revealed that the subsequent instances were getting created and were at the correct coordinates, but I couldn’t see them for some reason. After reading this question, I moved all the UMicroMeteor ctor code into BeginPlay() and suddenly everyone showed up perfectly.

Questions:

  1. What code exactly do I need to have in a Component’s BeginPlay() to allow it to be visible when created at runtime?
  2. Assuming the underlying issue was that the CDO paradigm causes ctors to only ever be called once, why couldn’t all instances of UMicroMeteor use the same mesh data? The mesh itself wasn’t changing per instance so I would expect the CDO to have the lone instance of mesh data that all the instances created from it then share, and each instance’s unique transform moves its vertices (initially structured by the shared mesh data) out to wherever they need to be.
  3. Following the question above, assuming that the mesh data is the only set of vertices (i.e. USceneComponents don’t have their own vertices informed by a UStaticMeshComponent but rather the UStaticMeshComponent associated with a USceneComponent represents its only vertices) and therefore all instances can’t share it simultaneously, why wasn’t the only visible UMicroMeteor the latest one to be created? In this scenario, I’d imagine each instance sharing the same mesh data and then applying a unique transform to it in the order the instances were created – this should have led to a noticeable progression of the mesh data from the first meteor position to the last, but instead I only ever saw the first.

I think UMicroMeteorComponent needs to be an Actor. I don’t think Components can have subobjects in this way.

Then in your actor spawn instances of the MicroMeteor actor.

I’m trying to complete the ‘extra credit’ for the Components & Collision tutorial [Components and Collision | Unreal Engine Documentation] that asks the reader to: “Build a Component that spawns up to three children, each of which despawn on their own after a set amount of time” – the UIounTorchComponent is the Component spawning three UMicroMeteorComponents. Do you mean that Component constructors specifically can’t have subobjects in the same way as Actors, or that Components shouldn’t be creating subobjects at all?

I think components might be ok to have subobjects but not other components. Reading the task I think it’s asking for a component that spawns 3 other single components in beginplay. Since your micro meteors are made of a sphere collision, static mesh + particle system, it needs to be an Actor.