I would not recommend to change the component creation method using the following:
UActorComponent* MyNativeComponentDynamicInstance = NewObject<USceneComponent>(this, UMyNativeComponent::StaticClass());
MyNativeComponentDynamicInstance->SetupAttachment(GetRootComponent());
// AddInstanceComponent(MyNativeComponentDynamicInstance) is the same as the 2 lines below
MyNativeComponentDynamicInstance->CreationMethod = EComponentCreationMethod::Instance;
AddOwnedComponent(MyNativeComponentDynamicInstance);
The reason is that you will break lots of editor mechanisms, for example, setting ‘Instance’ creation method allows the user to delete the component manually in the detail panel, which will lead to crashs if you keep a reference to it somewhere in your native code (and you certainly do). Also, it could break other things such as undo/redo…
The solution if you have only one instanced component is to define a property to a USceneComponent with ‘EditInstanceOnly’ flag (or a UActorComponent, but my previous example use a scene component, so I continue my explanation with that!), set it to a category that you choose to hide in class, and assign the value you dynamically created to the property:
UCLASS(Blueprintable, BlueprintType, HideCategories="MyActorInternals")
class AMyActor : public AActor
{
GENERATED_UCLASS_BODY()
public:
// Button in editor to create the dynamic instance
UFUNCTION(BlueprintCallable, CallInEditor)
void CreateMyDynamicComponentInstance()
{
UActorComponent* MyNativeComponentDynamicInstance = NewObject<USceneComponent>(this, UMyNativeComponent::StaticClass());
MyNativeComponentDynamicInstance->SetupAttachment(GetRootComponent());
AddOwnedComponent(MyNativeComponentDynamicInstance);
#if WITH_EDITOR
// The trick to display the dynamically created component in details panel!
DynamicComponent = MyNativeComponentDynamicInstance
#endif
}
#if WITH_EDITORONLY_DATA
private:
UPROPERTY(EditInstanceOnly, Category="MyActorInternals")
USceneComponent* DynamicComponent { nullptr };
#endif
};
With this method, component cannot be deleted by user, and undo/redo works correctly.
If you have multiples dynamic components, well, it’s more complicated. [Shmoopy1701] mentioned that the solution was to define a property with TArray type (or TMap / TSet), but unfortunally, it do not work with UE v5.1, maybe it was working before, DK.
// Nope, dynamically created components assigned to this property will not show in details panel :(
UPROPERTY(EditInstanceOnly, Category="MyActorInternals")
TArray<USceneComponent*> DynamicComponents;
The reason is that unlike ‘simple’ properties, details panel code looks in CDO’s array property if it contains the pointer to our component, which it do not have, as it’s the actor instance array property whose have it! See ‘SSCSEditor::BuildSubTreeForActorNode’ and the call to ‘FComponentEditorUtils::GetPropertyForEditableNativeComponent’ for implementation details.
The solution I ended up with is to reserve a bunch of UPROPERTY in the base class… This work if you know in advance what is the maximum number of dynamic components you will create, and if this number is not too big:
UCLASS(Blueprintable, BlueprintType, HideCategories="MyActorInternals")
class AMyActor : public AActor
{
GENERATED_UCLASS_BODY()
public:
// Button in editor to create the dynamic instance
UFUNCTION(BlueprintCallable, CallInEditor)
void CreateMyDynamicComponentInstance()
{
UActorComponent* MyNativeComponentDynamicInstance = NewObject<USceneComponent>(this, UMyNativeComponent::StaticClass());
MyNativeComponentDynamicInstance->SetupAttachment(GetRootComponent());
AddOwnedComponent(MyNativeComponentDynamicInstance);
#if WITH_EDITOR
if (DynamicComponent1 == nullptr) { DynamicComponent1 = MyNativeComponentDynamicInstance; }
else if (DynamicComponent2 == nullptr) { DynamicComponent2 = MyNativeComponentDynamicInstance; }
else if (DynamicComponent3 == nullptr) { DynamicComponent3 = MyNativeComponentDynamicInstance; }
else { /* Log some warning/error to user, if no more free dynamic property is available, the component will still be created in actor, but it will not be visible in detail panel */ }
#endif
}
#if WITH_EDITORONLY_DATA
private: /* Allows to display in detail panels the 3 first dynamically created components */
UPROPERTY(EditInstanceOnly, Category="MyActorInternals")
USceneComponent* DynamicComponent1 { nullptr };
UPROPERTY(EditInstanceOnly, Category="MyActorInternals")
USceneComponent* DynamicComponent2 { nullptr };
UPROPERTY(EditInstanceOnly, Category="MyActorInternals")
USceneComponent* DynamicComponent3 { nullptr };
#endif
};
Still, it’s kind of hackish way of doing…