Details panel for components created by components (all in C++)

Hello, I’m trying to set up a reusable hierarchy of components, I mean by that a component that will instantiate and attach to itself other component
The goal is to factorize code, in my precise case for everything related to the arms of a character, have an arm mesh, a motion controller, and other custom components packed into one “arm” component so I can manage right and left arm without code duplication
To simplify things let’s say I have one character c++ class, that is inherited by a blueprint. In this class I instantiate a custom scene component, and this custom scene component will instantiate a mesh component and attach it to itself.
The problem is when I open the character blueprint, I don’t have anything in the details tab for the mesh component. Some of its properties are available through the details tab of its parent scene component, but not at the same level of detail that I would get by instantiating the mesh directly from the character.
Is there a way to access this details ? Is it a bug of the editor ? Is my approach correct ? maybe the way I instantiate the child mesh component is not the best way ?
Thank you in advance for you help !

MyCharacter.h :

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "MySceneComponent.h"
#include "MyCharacter.generated.h"

UCLASS()
class TESTCOMPONENTS_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMyCharacter();

protected:
	UPROPERTY(VisibleAnywhere)
	UMySceneComponent* MySceneComponentInstance;

	UPROPERTY(VisibleAnywhere)
	USkeletalMeshComponent* MySkeletalMeshComponent;
};

MyCharacter.cpp :

#include "MyCharacter.h"


// Sets default values
AMyCharacter::AMyCharacter()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	MySceneComponentInstance = CreateDefaultSubobject<UMySceneComponent>(TEXT("MySceneComponent"));
	MySceneComponentInstance->SetupAttachment(RootComponent);

	MySkeletalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MySkeletalMeshComponent"));
	MySkeletalMeshComponent->SetupAttachment(RootComponent);
}

MySceneComponent.h :

#pragma once

#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "MySceneComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class TESTCOMPONENTS_API UMySceneComponent : public USceneComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	UMySceneComponent();

protected:
	UPROPERTY(VisibleAnywhere)
	USkeletalMeshComponent* MySubSkeletalMeshComponent;
};

MySceneComponent.cpp :

#include "MySceneComponent.h"


// Sets default values for this component's properties
UMySceneComponent::UMySceneComponent()
{
	// 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;
	bEditableWhenInherited = true;

	MySubSkeletalMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MySubSkeletalMeshComponent"));
	MySubSkeletalMeshComponent->SetupAttachment(this);
	MySubSkeletalMeshComponent->bEditableWhenInherited = true;
}

The resulting views in the editor for the character blueprint, taking the Collision Category as an example :

when selecting the sub skeletal mesh :

when selecting the my scene component (parent of the skeletal mesh) :

when selecting the skeletal mesh directly instantiated by the character :

I’ve tried all these before posting, but it doesn’t solve my problem.
The difference relies only in the fact that in one case the mesh component is declared and created in the character derived class, and in the other case it is done in the component derived class

My next try was to use the ObjectInitializer from the character constructor and pass it down to the component so the sub component is created in the name of the character class, but that did not solve the problem

in MyCharacter.cpp :

AMyCharacter::AMyCharacter(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	MySceneComponentInstance = ObjectInitializer.CreateDefaultSubobject<UMySceneComponent>(this, TEXT("MySceneComponent"));
	MySceneComponentInstance->SetupAttachment(RootComponent);
	MySceneComponentInstance->CreateChildrenComponents(ObjectInitializer, this);

	MySkeletalMeshComponent = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("MySkeletalMeshComponent"));
	MySkeletalMeshComponent->SetupAttachment(MySceneComponentInstance);
}

and in MySceneComponent.cpp :

void UMySceneComponent::CreateChildrenComponents(const FObjectInitializer& ObjectInitializer, UObject* parent)
{
	MySubSkeletalMeshComponent = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(parent, TEXT("MySubSkeletalMeshComponent"));
	MySubSkeletalMeshComponent->SetupAttachment(this);
	MySubSkeletalMeshComponent->bEditableWhenInherited = true;
}

Following the code, the 2 mesh components should be created exactly in the same way, but I guess the fact that the property is declared in the component is the key
I’ve tried to find a meta parameter for the property that would solve the problem, but without any luck

Sorry, I tried again, and it kind of solves my problem when I specify EditAnywhere for the sub mesh property, as all of its parameter will be accessible through the MyComponent details panel.

This is not really the behaviour I was aiming for, as I do not want the reference to the mesh to be changed.

VisibleAnywhere should allow me to change all the properties of the sub component without being able to change the reference, which is exactly what I want, so should this be considered as a bug ?

As a note, the sub component property can be protected (which is kind of illogical I agree)

Also I found there is the Instanced meta tag that specifies the property is a component reference, but it doesn’t seem to have any effect on my issue

Sidenot on the sidenote, Instanced meta specifier is superfluous as UActorComponent is declared as DefaultToInstanced

Regarding the VisibleAnywhere vs EditAnywhere, try EditDefaultsOnly or EditInstanceOnly. If not Im sure there are others you can try. Ill try this one once I get off from work.

Thanks for your answer !

I haven’t found a proper solution but I have advanced a bit in my comprehension of how things work :

BlueprintReadOnly/ReadWrite : this should be only related to graphs, and without surprise have no effect on our issue

public/protected/private : the derivation into blueprint is done with the unreal reflection system and will/can get rid of these scopes, our issue is not related to this

One thing that ends up being a workaround is to put the variable as EditAnywhere. In this case, within the parent component details panel, you will have access to all details of your subcomponent.

You will also be able to change the reference to the subcomponent, which is not something I wanted, but at least I have access to the subcomponent parameters

windows only

another thing I discovered and should be carefully taken care of, is that if you change your C++ properties after you have created a derived blueprint, properties can be messed up with because the blueprint will serialize them and can mix things up when loading the BP and deserialize the BP parameters after your changes.
Typically sub components will be set to null, but I have also seen two components of the same type have their parameters exchanged ! The solution for this is to declare the property with the specifier SkipSerialization

I’m afraid I’ve had this problem for years.

I’m currently re-creating the project I was working on back then, trying to comply to the new UE4 v4.21.2, and I’m running into the exact same problems as back then, and the exact same problem as you.

There’s a topic here where people discuss something similar:
https://forums.unrealengine.com/development-discussion/c-gameplay-programming/57024-inherited-component-details-not-showing-in-blueprint

There’s also this:

====

It seems nobody seems to know quite how to resolve this specific problem, and/or nobody gets the same repro as we do. I find it somewhat comforting to know I’m not alone, but it sucks that in all this time there doesn’t seem to have been a solution for this.

None of the solutions work for me: BlueprintReadOnly/ReadWrite, Visible/EditAnywhere, Category setting, public/protected/private, using the FObjectInitializer for the Constructor, reparenting, redefining, renaming, recompiling, recreating the derived Blueprint, adding an Instance Component instead. Nothing.

I’ve been researching the issue for a couple of hours again just now, and I’m very close to just creating the components I need through Blueprint and trying to write whatever functionality I want in C++, although I’m pretty sure that’s not actually a good idea. Just exploring new avenues as this one seems a dead end for me, at the current time.

Sorry.

Good luck! If you ever do find a solution, please contact me ASAP.

Search for the topic “details panel” here on AnswerHub and you’ll find many, many familiar problems. x)

Other places like this tutorial:

Seem to indicate that this “just works” for most people, and that the problem we’re facing is somewhat unique.

@developpeur2000 going on a hunch, what OS are you running on? Mac? Windows? Windows through a Mac Bootcamp?

True – in my tests I keep recreating the Blueprint to get a “fresh start” and initialise the starting conditions of my problem. I spotted a sneaky “MySubSkeletalMeshComponent->bEditableWhenInherited = true;” in your post that I hadn’t caught before. My last hope for my own problem…!

Unfortunately, no dice. They’re not showing up, neither in their own Details panels, nor in those of their parents (everything is empty).

I do have a work-around in mind though: I can expose regular variables to the Details panel of the main Blueprint itself, for example Material asset selectors. Perhaps I’ll use this area to set the Mesh and other parameters I care about for my C+±created components, and simply hope that, one day, this bug will finally be squashed.

Thanks for thinking with me!

Because of all these issues I ended up exposing only a few things but not the subcomponents, and set every subcomponent parameters in C++

for example I expose a transform, so it can be set at the parent component level, and at begin play I apply the transform to the subcomponent, it was much less of a hassle !

This subcomponent was added using Blueprint, or still through C++?

in C++

in my arm component .h file :

protected:
	UPROPERTY(SkipSerialization)
	USkeletalMeshComponent* ArmSkeletalMesh;
public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FTransform ArmMeshRelativeTransform;

in my arm component .cpp file :

#define MAKE_SUBCOMPONENT_NAME(ParentName, SubObjectName) FName(*FString::Printf(TEXT("%s_%s"), *ParentName, TEXT(SubObjectName)))

UArmComponent::UArmComponent(const FObjectInitializer& ObjectInitializer)
{
	// arm 1st person mesh
	FString ArmName = GetName();
	ArmSkeletalMesh = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, MAKE_SUBCOMPONENT_NAME(ArmName, "ArmSkeletalMesh"), true);
	ArmSkeletalMesh->SetupAttachment(this);
	ArmSkeletalMesh->bEditableWhenInherited = true;
   // parameters that used to be set in the blueprint
   // but they have no reason to be changed
	ArmSkeletalMesh->bOnlyOwnerSee = true;
	ArmSkeletalMesh->CastShadow = false;
	ArmSkeletalMesh->bCastDynamicShadow = false;
	ArmSkeletalMesh->bAffectDynamicIndirectLighting = false;
	ArmSkeletalMesh->bAffectDistanceFieldLighting = false;
	ArmSkeletalMesh->bCastStaticShadow = false;
}

void UArmComponent::BeginPlay()
{
	Super::BeginPlay();

   // apply the transform that was set in the blueprint
	ArmSkeletalMesh->SetRelativeTransform(ArmMeshRelativeTransform);
}

I hope this helps you

Thank you. Another good work-around.

For my case, it’s finally been resolved thanks to a friend looking at my code.

Turns out it was extremely stupid in the end, as expected. x)

See the first link in my first post in this thread.

Hope your problem can be truly resolved, soon, too!