Using Blueprints as Asset Selectors for C++

My goal is to create my C++ classes and create the classes components, etc in the C++ class, then derive a blueprint from those C++ classes and in that blueprint assign assets (static meshes, skeletal meshes, materials, etc). My ideal workflow would be to allow the blueprint to act as the selector of assets, while the C++ does the heavy work, as I much prefer to work in C++ over blueprints for logic.

Right now that’s not working out very well, however,

Current version of the class with unrelated parts removed:

UCLASS()
class HEXINSTANTIATION_API AInstanceCreator : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AInstanceCreator(const FObjectInitializer& ObjectInitializer);

public:
	// UInstancedStaticMeshComponents
	UInstancedStaticMeshComponent* InstancedMesh1;
	UInstancedStaticMeshComponent* InstancedMesh2;
	UInstancedStaticMeshComponent* InstancedMesh3;

public:
	// Static Mesh that we want to use for our UInstancedStaticMeshComponent
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Instantiatables)
	UStaticMesh* Mesh1;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Instantiatables)
	UStaticMesh* Mesh2;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Instantiatables)
	UStaticMesh* Mesh3;
};

Then in the .cpp file:

// Sets default values for this actor's properties
AInstanceCreator::AInstanceCreator(const FObjectInitializer& ObjectInitializer)
{
	PrimaryActorTick.bCanEverTick = false;

	InstancedMesh1 = ObjectInitializer.CreateDefaultSubobject<UInstancedStaticMeshComponent>(this, TEXT("Instanced Mesh 1"));
	InstancedMesh2 = ObjectInitializer.CreateDefaultSubobject<UInstancedStaticMeshComponent>(this, TEXT("Instanced Mesh 2"));
	InstancedMesh3 = ObjectInitializer.CreateDefaultSubobject<UInstancedStaticMeshComponent>(this, TEXT("Instanced Mesh 3"));

	RootComponent = InstancedMesh1;
	InstancedMesh2->AttachParent = RootComponent;
	InstancedMesh3->AttachParent = RootComponent;

	InstancedMesh1->SetStaticMesh(Mesh1);
	InstancedMesh2->SetStaticMesh(Mesh2);
	InstancedMesh3->SetStaticMesh(Mesh3);
}

My feeling is that because I haven’t set the Mesh in the constructor, the assignment is meaningless and then the constructor never gets visited again later, so where should I put the InstancedMesh1->SetStaticMesh(Mesh1); code so that it picks up when I add/change a Mesh in a blueprint? (If it’s important, I also want to use the OnConstruction function in C++, so i need the solution to work cooperatively with that function, I don’t think setting the initialization in OnConstruction would be the appropriate workflow, since I don’t always want to use OnConstructed, please correct me if I’m wrong, though.)

Thanks you for the time and assistance.

Your suspicions are correct. This essentially is a question of how to correctly initialize internal object state based on property values, something I would really like a tutorial on myself. There are so many Pre_/PostInit_/PostLoad_ methods in UObject and it’s really unclear which ones to use for what.

Here’s the little I know.

UObject::PostInitProperties is, despite the name, absolutely not what you’re looking for. It is called too soon, before properties are loaded from the source, whatever that may be.

AActor::PostInitializeComponents seems to work, so long as your class derives from AActor and you only want to do the initialization in-game, not on editor reload/property change. It feels like a hack though when there are no components involved.

There is also UObject::PostLoad, but I haven’t played around with that as yet, the docs description is a little unhelpful.

Okay so I think I’ve given some misleading info due to a slight difference in your use case and my own aims. It seems that UObject::PostInitProperties() will reflect the values set in a blueprint derived from your class and so may be precisely what you need after all.

It’s a class/instance distinction. A blueprint is, I think, essentially defining a derived class with some different defaults. If you will only ever set these mesh properties at a blueprint class level, you should be fine. However, it seems that object/instance level properties (ie. values set in the details panel for a particular object instance placed in the level) have not yet been loaded at the point PostInitProperties is called.

So perhaps PostInitProperties will work for you in this case, however to me it seems inherently wrong to initialize an object based on property values from the class default, which may be about to get overwritten by those that were specified on the object instance level.

I’d really like some clarification on this stuff.

I’ll look into the PostInitProperties() function, thank you for that. Found a potential work-around that might benefit you. Seems like OnConstruction(const FTransform& Transform) might be something that will help you since you are looking for modified properties on instances of a class. The nuance regarding OnConstruction is that it only works for things derived from AActor, which should cover almost anything you would want to put in a level anyways… It is also the function that the blueprint construction script is based off of, so any movement or property changes will recall the function.

I agree though, I would really like a nice documentation page that covers all these different types of initializers, or a nice comment below this :P.

Thanks, OnConstruction looks useful, especially regarding reaction to property changes. Still, it’s yet another one to add to the long list of initialization overrides that I don’t fully understand when to use. I’ll get there eventually I guess…