Yes, thatās exactly my context. But if I can make it as generic and reusable as possible Iām all up for it, even if itās more work
For now I have ended up with this component and it seems to work ok, not great but ok. At least it does what I wanted. The only downside is the Instanced specifier for InstancedMyClassVariable which allows you to change this value independently from InstanceClass. And I cannot change it to VisibleAnywhere either. That prevents the InstancedMyClassVariable members to show up in the editor and therefore they would not be editable.
.h
// Copyright 2016 UnrealEverything. All Rights Reserved.
#pragma once
#include "Components/ActorComponent.h"
#include "InstanceHelperComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class PROJECT_API UInstanceHelperComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UInstanceHelperComponent();
virtual ~UInstanceHelperComponent();
virtual void PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent) override;
UPROPERTY(BlueprintReadOnly, Category = "Instancing", EditAnywhere)
TSubclassOf<UObject> InstanceClass;
UPROPERTY(BlueprintReadOnly, Category = "Instancing", EditAnywhere, Instanced)
UObject* InstancedMyClassVariable;
};
.cpp
// Copyright 2016 UnrealEverything. All Rights Reserved.
#include "Project.h"
#include "InstanceHelperComponent.h"
// Sets default values for this component's properties
UInstanceHelperComponent::UInstanceHelperComponent()
: Super()
{
// 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.
bWantsBeginPlay = false;
PrimaryComponentTick.bCanEverTick = false;
InstanceClass = NULL;
InstancedMyClassVariable = nullptr;
}
UInstanceHelperComponent::~UInstanceHelperComponent()
{
}
void UInstanceHelperComponent::PostEditChangeProperty(FPropertyChangedEvent & PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
UProperty* MemberProperty = PropertyChangedEvent.MemberProperty;
if (MemberProperty)
{
const FName MemberPropertyName(MemberProperty->GetFName());
if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UInstanceHelperComponent, InstanceClass))
{
UClass* Class = *InstanceClass;
if (Class)
{
//TODO specific outer, other arguments, etc.
InstancedMyClassVariable = NewObject<UObject>(InstancedMyClassVariable ? InstancedMyClassVariable->GetOuter() : GetOuter(), Class);
}
else
{
InstancedMyClassVariable = nullptr;
}
}
else
{
//handling for other property names
}
}
else
{
//errorhandling for MemberProperty == nullptr
}
}
I guess an engine mod would be the cleanest solution, Iād only have to lift this restriction (and hope that it does not cause any problems down the line). After spending some time searching and reading some code (starting with UBlueprint and finding my way to FBPVariableDescription, FKismetCompilerContext::CreateClassVariablesFromBlueprint most importantly FBlueprintVarActionDetails::CustomizeDetails) it may be as simple as adding a new custom row to the āVariableā category which is a checkbox like āEditableā and controls whether to add the Instanced specifier to the property flags or not. So using detail customization. And then make sure it actually has an effect and is not disabled anywhereā¦
Iāll investigate this route further and can possibly create a pull request for this if Epic is willing to change their mind and expose this flag.
Edit: After a bit more testing, the above component approach does not really work either. A blueprint Actor class with one of these components which has been placed in the editor does not properly update InstancedMyClassVariable when InstanceClass is changed e.g. when changing from class āNoneā while InstancedMyClassVariable is also None to any other class, InstancedMyClassVariable remains unchanged (at least in the Editor UI). Not sure whether I am missing some update function or why it doesnāt update.