Refresh / destroy an Actor's Components in c++ changing a property in the engine

Hello, I’m trying to refresh an actor when I change a property in the engine.
The actor owns a property (“Spawning Rooms”) telling how many rooms it should spawn.
Through PostEditChangeProperty I can refresh the actor, regenerating the rooms.
To do so, I call a DestroyComponent() to the pointer to the previously saved SceneComponents, and then regenerate the whole dungeon.

Doing so, the rooms initially created (and only them) disappear forever, while the rooms generated later appear and disappear correctly.

I have a main actor “Floor” and a SceneComponent “Room”.

Floor.h

#include "GameFramework/Actor.h"
#include "YARGRoom.h"
#include "YARGFloor.generated.h"

UCLASS()
class YARGPROTO_API AYARGFloor : public AActor
{
	GENERATED_BODY()
	
public:	
	AYARGFloor(const class FObjectInitializer& ObjectInitializer);

	virtual void BeginPlay() override;
	
	virtual void Tick( float DeltaSeconds ) override;

	virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;

	virtual void GenerateFloor();
	virtual void CleanAndGenerate();
	
protected:

	TArray<UYARGRoom*> RoomsArray;

	UPROPERTY(EditAnywhere, Category="Floor Options")
	uint32 TileSize;
	UPROPERTY(EditAnywhere, Category = "Floor Options")
	uint32 WorldSize;
	UPROPERTY(EditAnywhere, Category = "Floor Options")
	uint32 MinRoomSide;
	UPROPERTY(EditAnywhere, Category = "Floor Options")
	uint32 MaxRoomSide;
	UPROPERTY(EditAnywhere, Category = "Floor Options")
	uint32 SpawningRooms;
	UPROPERTY(EditAnywhere, Category = "Floor Options")
	uint32 SpawningEllipseWidth;
	UPROPERTY(EditAnywhere, Category = "Floor Options")
	uint32 SpawningEllipseHeight;

private:

};

Floor.cpp

#include "YARGProto.h"
#include "YARGFloor.h"
#include "YARGMath.h"


AYARGFloor::AYARGFloor(const class FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
	, TileSize(100)
	, WorldSize(10000)
	, MinRoomSide(2)
	, MaxRoomSide(5)
	, SpawningRooms(5)
	, SpawningEllipseWidth(5000)
	, SpawningEllipseHeight(5000)
{
	GenerateFloor();
	PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void AYARGFloor::BeginPlay()
{
	Super::BeginPlay();
}

void AYARGFloor::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );
}

void AYARGFloor::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent)
{
	CleanAndGenerate();

	Super::PostEditChangeProperty(PropertyChangedEvent);
}

void AYARGFloor::CleanAndGenerate()
{
	for (UYARGRoom* pRoom : RoomsArray)
	{
		pRoom->DestroyComponent();
	}
	RoomsArray.Empty();
	GenerateFloor();
}

void AYARGFloor::GenerateFloor()
{
	RoomsArray.Init(SpawningRooms);

	for (uint32 uiRoomIndex = 0; uiRoomIndex < SpawningRooms; ++uiRoomIndex)
	{
		UYARGRoom* NewRoom = NewObject<UYARGRoom>(this, *FString::Printf(TEXT("Room %u"), uiRoomIndex));
		RoomsArray[uiRoomIndex] = NewRoom;
		NewRoom->AttachTo(RootComponent);
	}
}

The SceneComponent Room has nothing specific.

Initial situation:

I correctly see all the 5 default rooms.

From there, if I set more than 5 rooms:

while if I set less than 5 rooms:

Note: if I set 6 , 8 and again 6, i see 1, 3 and 1 rooms, so it doesn’t seems a name conflict.

Any help is appreciated, thanks in advance.

Hi . Dynamically creating components in C++ is a bit more involved than just creating the UObject and attaching it. You have to call a few other procedures as well. Here’s an example snippet from some code that I use to dynamically create static mesh components, along with a separate hit box component for each, for something like a chess board Actor:

			const FTransform LocalTransform(
				FRotator::ZeroRotator,
				Position + FVector(0.f, 0.f, VisualHeight),
				FVector(1.f, 1.f, 1.f));
			const FTransform SpawnedTransform = LocalTransform * GetTransform();
			auto* NewMeshComponent = NewObject<UStaticMeshComponent>(this);
			NewMeshComponent->CreationMethod = EComponentCreationMethod::Native;
			if (TileMesh) NewMeshComponent->SetStaticMesh(TileMesh);
			NewMeshComponent->OnComponentCreated();
			NewMeshComponent->AttachTo(RootComponent);
			NewMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
			NewMeshComponent->bGenerateOverlapEvents = false;
			NewMeshComponent->SetRelativeTransform(TileMeshLocalTransform * LocalTransform);
			NewMeshComponent->SetMobility(EComponentMobility::Static);
			NewMeshComponent->RegisterComponent();
			auto* NewBoxComponent = NewObject<UBoxComponent>(this);
			NewBoxComponent->CreationMethod = EComponentCreationMethod::Native;
			NewBoxComponent->SetBoxExtent(TileHitBoxExtents);
			NewBoxComponent->OnComponentCreated();
			NewBoxComponent->AttachTo(RootComponent);
			NewBoxComponent->SetRelativeTransform(TileHitBoxLocalTransform * LocalTransform);
			NewBoxComponent->SetCollisionObjectType(COLLISION_INTERACTION);
			NewBoxComponent->bGenerateOverlapEvents = false;
			NewBoxComponent->SetMobility(EComponentMobility::Static);
			NewBoxComponent->RegisterComponent();

I can see some methods I didn’t call on my snip. Anyway, I followed the code on https://docs.unrealengine.com/latest/INT/Programming/Tutorials/Components/1/index.html so I thought that was all I needed. What about the destruction of the component? And in your case, do your components get a name in the editor?
Is there some difference in creating the components in the costructor of the actor, or there is a better time for that?

It’s a bit tricky. If you’re creating the component in the constructor/initializer for your Actor, you don’t have to call as many procedures – the engine will do it for you when the Actor is spawned. However, components created in the constructor are limited to so-called “default subobjects”: the components are considered an important part of the actor, will show up for editing in the Blueprint editor/defaults editor, and you can only use default values to determine what happens. Basically, you can only read from the default values of your class in a constructor, so you are pretty limited in terms of procedural generation for what you can do with default subobjects. Plus, there is usually a fixed number of default subobjects in a class created in a constructor – you’d rarely create an array of them, especially one of arbitrary size determined by some other data (I’m pretty sure something bad will happen if you try this).

However, you’re free to create components elsewhere in your Actor, such as when parameters are modified, provided you do the correct things for your component’s lifecycle. This means calling a few more procedures manually and setting some data.

As for destruction, the regular DestroyComponent will work just fine – it handles everything for you.

So, if I’d want to create a component in a constructor, provided that it won’t change later, what should be done?

I made a few changes: put the GenerateRoom() in the BeginPlay(), used both Attach and Register, and the ConstructObject to generate the Rooms. This way the Floor behaves correctly . except when in the editor’s viewport because it shows nothing, and only after a PostEditChangedProperty it spawns the correct number of subcomponents.
I saw that BeginPlay doesn’t get called in the editor; is there an equivalent method?

I thought that every component should have a name provided, but I noticed yours haven’t. Is there a difference?

Try overriding OnConstruction and PostEditPropertyChange (can’t remember if that’s the right name or not).

If you don’t provide a name to a component, one will be generated for you. Explicit names are only required for default subobjects.