Can i create & attach a component inside a component ?

Hello,

I’m trying to create a component inside another component and it doesn’t work.

More precisely, i’m setting up in C++ my own class of motion controller component inheriting from UMotionControllerComponent.
The idea is to add the hand (USkeletalMeshComponent) and laser (UStaticMeshComponent) and attach them properly to get a ready to use motion controller for my vr game.

The problem is when i do this, it compiles and launch fine, the components are displayed in the blueprint, but the meshes are not.
And when trying to manually add my motion controller to a BP and compiling the BP, the editor crashes.

Is it expected ? Am i not supposed to create & attach component within a component ?

Thanks
Cedric

[EDIT]:
A few comments about this thread.
I’ve mark 3dRaven’s answer as the solution because technically it solves my initial question (crash problem).

But for anyone bumping here and having read our long discussion, here’s what i ended up doing to be able to use UMotionController whatever the currently possessed pawn.
After a lot of tests, i finally put back the VR logic (UMotionController, hand meshes, etc.) in the main player pawn (the one that is possessed by default).
And to avoid freezing the hands when possessing another pawn, i followed the UMotionController code in the engine.
It turns out that, after a chain of conditions, the UMotionController is active if its top owner is controlled locally.
As my main pawn wasn’t controlled when unpossessed, i set its owner to be the player controller.
That way, even when not possessed, the top owner always remains the PC and the UMotionController remains active.
So at the end, all my code needed from the start was that small line in the main pawn’s class:

if (world) SetOwner(world->GetFirstPlayerController());

Again, huge thanks to 3dRaven for his time and help :slight_smile:

1 Like

Nested components can only be accessed from the parent components details panel.

The nested workflow is unfortunately not really as user friendly as adding components by hand.

Sometimes it’s better to flat out just add them to the actor and leave hook logic in the form of uproperties that can be set from the parent actor to chain the components together.

I hope that some day we will be able to have nested components that behave the the blueprint ones.

Thanks,

I installed the full debug symbols and made some more tests.

In fact, it is not related to the attachment.

I just have to declare and create a skeletal mesh component inside a class inheriting from UMotionControllerComponent.

//Header:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = “test”)
USkeletalMeshComponent* mccHandMesh;

//cpp:
mccHandMesh = CreateDefaultSubobject(TEXT(“mccHandMesh”));

This is enough to get a crash when adding my custom motion controller component to a simple Character BP.

It crashes over some map checking, but the strange thing is that the stack suggests this comes from some garbage collection:

[Inline Frame] UnrealEditor-Engine.dll!TMapBase<UObject *,FAnimUpdateRateManager::FAnimUpdateRateParametersTracker *,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<UObject *,FAnimUpdateRateManager::FAnimUpdateRateParametersTracker *,0>>::FindChecked(const UObject *) Line 656 C++
UnrealEditor-Engine.dll!FAnimUpdateRateManager::CleanupUpdateRateParametersRef(USkinnedMeshComponent * SkinnedComponent) Line 173 C++
UnrealEditor-Engine.dll!USkinnedMeshComponent::OnUnregister() Line 650 C++
UnrealEditor-Engine.dll!UActorComponent::ExecuteUnregisterEvents() Line 1707 C++
UnrealEditor-Engine.dll!UActorComponent::BeginDestroy() Line 726 C++
UnrealEditor-Engine.dll!UPrimitiveComponent::BeginDestroy() Line 1451 C++
UnrealEditor-Engine.dll!USkinnedMeshComponent::BeginDestroy() Line 3785 C++
UnrealEditor-CoreUObject.dll!UObject::ConditionalBeginDestroy() Line 1024 C++
UnrealEditor-CoreUObject.dll!UnhashUnreachableObjects(bool bUseTimeLimit, double TimeLimit) Line 2616 C++
UnrealEditor-CoreUObject.dll!CollectGarbageInternal(EObjectFlags KeepFlags, bool bPerformFullPurge) Line 2422 C++
UnrealEditor-CoreUObject.dll!CollectGarbage(EObjectFlags KeepFlags, bool bPerformFullPurge) Line 2671 C++
UnrealEditor-Kismet.dll!FBlueprintCompilationManagerImpl::CompileSynchronouslyImpl(const FBPCompileRequestInternal & Request) Line 315 C++
UnrealEditor-Kismet.dll!FBlueprintCompilationManager::CompileSynchronously(const FBPCompileRequest & Request) Line 3294 C++
UnrealEditor-UnrealEd.dll!FKismetEditorUtilities::CompileBlueprint(UBlueprint * BlueprintObj, EBlueprintCompileOptions CompileFlags, FCompilerResultsLog * pResults) Line 763 C++
UnrealEditor-Kismet.dll!FBlueprintEditor::Compile() Line 4060 C++

Any idea why it would behaves this way ?

Thanks
Cedric

Leave the uproperty like this

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = “test”)
class USkeletalMeshComponent* mccHandMesh;

And do not create the subobject in the component (in the CDO). It seems to crash the engine. Instead create it in the parent and hook it up while being uninitialized.

set

If you need to do this through c++ then do it on the side of the owner (main actor) it should keep the engine in check. Worked on my end.

Hi,

Again, thanks a lot for the pointers and helpful advices :slight_smile:
I did what you suggested and it solved the crash problem.
But from there on, things got weirder and weirder.

While the crash was gone, the attachment were still not working properly.
Plus, although the correct BP properties were defined (EditAnywhere, BlueprintReadWrite), the details panel of the component remained invisible in the editor, as if they weren’ marked as BP exposed.

So after having spent a few hours (trials/error passing parts of the code between the constructor and the BeginPlay function) to no avail, i decided to go for a flat and simple scheme: let the motion controller be a simple motion controller and put all the definition/logic in the parent actor, here, my PlayerController.
It can’t really get any simpler than this:

////////////////////////////////////////////
// LEFT HAND
////////////////////////////////////////////
// motion controller
mccHand_L = CreateDefaultSubobject<Ugen_MotionControllerComponent>(TEXT(“mccHand_L”));
mccHand_L->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
mccHand_L->SetTrackingSource(EControllerHand::Left);

// hand skeletal mesh
mccHandMesh_L = CreateDefaultSubobject(TEXT(“mccHandMesh_L”));
mccHandMesh_L->AttachToComponent(mccHand_L, FAttachmentTransformRules::KeepRelativeTransform);
mccHandMesh_L->SetRelativeLocation(FVector::ZeroVector);
mccHandMesh_L->SetRelativeRotation(FRotator(-90.f, 90.f, 0));

ConstructorHelpers::FObjectFinder SkeletalMeshHand_L(TEXT(“/Script/Engine.SkeletalMesh’/gen_Game/HTAB/meshes/translucent_hands/TranslucentHand_L_UE4.TranslucentHand_L_UE4’”));
mccHandMesh_L->SetSkeletalMesh(SkeletalMeshHand_L.Object);

// laser
laserMesh_L = CreateDefaultSubobject(TEXT(“laserMesh_L”));
laserMesh_L->SetStaticMesh(StaticMeshLaser.Object);
laserMesh_L->AttachToComponent(mccHandMesh_L, FAttachmentTransformRules::KeepRelativeTransform);
laserMesh_L->SetRelativeLocation(FVector(LASER_HAND_DISTANCE, 0.f, 0.f));
laserMesh_L->SetRelativeRotation(FRotator(-90.f, 0.f, 0.f));

// laser hit
laserHitMesh_L = CreateDefaultSubobject(TEXT(“laserHitMesh_L”));
laserHitMesh_L->SetStaticMesh(StaticMeshLaserHit.Object);
laserHitMesh_L->SetWorldScale3D(FVector(0.02f, 0.02f, 0.02f));

Now the components and their hierarchy (who is attached to who) are properly defined, they show in the detail panel so i can check that everything (the meshes, etc.) is correctly setup.

But now, for some reason, although everything seems to be correct, nothing is showing in the viewport (and of course, nothing in game either).
The viewport stays empty as if nothing graphical was defined.

I can compile the player controller BP (created from my custom PC class) with no error, but when i save it (and restart the constructor), i get a bunch of scary messages in the output:

Exception thrown at 0x00007FFAB98D039C in UnrealEditor.exe: Microsoft C++ exception: wil::ResultException at memory location 0x000000242F81DDA0.
Exception thrown at 0x00007FFAB98D039C in UnrealEditor.exe: Microsoft C++ exception: [rethrow] at memory location 0x0000000000000000.
Exception thrown at 0x00007FFAB98D039C in UnrealEditor.exe: Microsoft C++ exception: wil::ResultException at memory location 0x000000243039DA30.
Exception thrown at 0x00007FFAB98D039C in UnrealEditor.exe: Microsoft C++ exception: wil::ResultException at memory location 0x000000242F81DE10.
Exception thrown at 0x00007FFAB98D039C in UnrealEditor.exe: Microsoft C++ exception: [rethrow] at memory location 0x0000000000000000.

Probably related to my problem, but i don’t know what they mean.
The editor is not crashing, it’s just that nothing is displayed in the PC viewport.

I have been using this sort of code forever and never had any problem with it.
Is there some huge error in my code that is right in my face and i don’t see ? Wouldn’t be the first time :smiley:

Thanks
Cedric

Hierarchy is intact

Used the first person template so just ignore the template code

header

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "mcCharacter.generated.h"

class UInputComponent;
class USkeletalMeshComponent;
class USceneComponent;
class UCameraComponent;
class UAnimMontage;
class USoundBase;

// Declaration of the delegate that will be called when the Primary Action is triggered
// It is declared as dynamic so it can be accessed also in Blueprints
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUseItem);

UCLASS(config=Game)
class AmcCharacter : public ACharacter
{
	GENERATED_BODY()

	/** Pawn mesh: 1st person view (arms; seen only by self) */
	UPROPERTY(VisibleDefaultsOnly, Category=Mesh)
	USkeletalMeshComponent* Mesh1P;

	/** First person camera */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	UCameraComponent* FirstPersonCameraComponent;

public:
	AmcCharacter();

protected:
	virtual void BeginPlay();

public:
	/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
	float TurnRateGamepad;

	/** Delegate to whom anyone can subscribe to receive this event */
	UPROPERTY(BlueprintAssignable, Category = "Interaction")
	FOnUseItem OnUseItem;
protected:
	
	/** Fires a projectile. */
	void OnPrimaryAction();

	/** Handles moving forward/backward */
	void MoveForward(float Val);

	/** Handles strafing movement, left and right */
	void MoveRight(float Val);

	/**
	 * Called via input to turn at a given rate.
	 * @param Rate	This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
	 */
	void TurnAtRate(float Rate);

	/**
	 * Called via input to turn look up/down at a given rate.
	 * @param Rate	This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
	 */
	void LookUpAtRate(float Rate);

	struct TouchData
	{
		TouchData() { bIsPressed = false;Location=FVector::ZeroVector;}
		bool bIsPressed;
		ETouchIndex::Type FingerIndex;
		FVector Location;
		bool bMoved;
	};
	void BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location);
	void EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location);
	void TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location);
	TouchData	TouchItem;
	
protected:
	// APawn interface
	virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;
	// End of APawn interface

	/* 
	 * Configures input for touchscreen devices if there is a valid touch interface for doing so 
	 *
	 * @param	InputComponent	The input component pointer to bind controls to
	 * @returns true if touch controls were enabled.
	 */
	bool EnableTouchscreenMovement(UInputComponent* InputComponent);

public:
	/** Returns Mesh1P subobject **/
	USkeletalMeshComponent* GetMesh1P() const { return Mesh1P; }
	/** Returns FirstPersonCameraComponent subobject **/
	UCameraComponent* GetFirstPersonCameraComponent() const { return FirstPersonCameraComponent; }

	UPROPERTY(BlueprintReadWrite, editAnywhere)
	USkeletalMeshComponent* mccHandMesh_L;

	UPROPERTY(BlueprintReadWrite, editAnywhere)
		UStaticMesh* StaticMeshLaser;

	UPROPERTY(BlueprintReadWrite, editAnywhere)
		UStaticMeshComponent* laserMesh_L;

	UPROPERTY(BlueprintReadWrite, editAnywhere)
		UStaticMeshComponent* laserHitMesh_L;

	UPROPERTY(BlueprintReadWrite, editAnywhere)
		UStaticMesh* StaticMeshLaserHit;

	UPROPERTY(BlueprintReadWrite, editAnywhere)
	float LASER_HAND_DISTANCE = 200;
	
	UPROPERTY(BlueprintReadWrite,editAnywhere)
		USkeletalMesh* SkeletalMeshHand_L;

	UPROPERTY(BlueprintReadWrite, editAnywhere)
	class Ugen_MotionControllerComponent* mccHand_L;

};

cpp

// Copyright Epic Games, Inc. All Rights Reserved.

#include "mcCharacter.h"
#include "mcProjectile.h"
#include "Animation/AnimInstance.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/InputSettings.h"

#include "gen_MotionControllerComponent.h"

//////////////////////////////////////////////////////////////////////////
// AmcCharacter

AmcCharacter::AmcCharacter()
{
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);

	// set our turn rates for input
	TurnRateGamepad = 45.f;

	// Create a CameraComponent	
	FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
	FirstPersonCameraComponent->SetupAttachment(GetCapsuleComponent());
	FirstPersonCameraComponent->SetRelativeLocation(FVector(-39.56f, 1.75f, 64.f)); // Position the camera
	FirstPersonCameraComponent->bUsePawnControlRotation = true;

	// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
	Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
	Mesh1P->SetOnlyOwnerSee(true);
	Mesh1P->SetupAttachment(FirstPersonCameraComponent);
	Mesh1P->bCastDynamicShadow = false;
	Mesh1P->CastShadow = false;
	Mesh1P->SetRelativeRotation(FRotator(1.9f, -19.19f, 5.2f));
	Mesh1P->SetRelativeLocation(FVector(-0.5f, -4.4f, -155.7f));




	////////////////////////////////////////////
	// LEFT HAND
	////////////////////////////////////////////
	// motion controller
	mccHand_L = CreateDefaultSubobject<Ugen_MotionControllerComponent>(TEXT("mccHand_L"));
	mccHand_L->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
	mccHand_L->SetTrackingSource(EControllerHand::Left);

	// hand skeletal mesh
	mccHandMesh_L = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("mccHandMesh_L"));
	mccHandMesh_L->AttachToComponent(mccHand_L, FAttachmentTransformRules::KeepRelativeTransform);
	mccHandMesh_L->SetRelativeLocation(FVector::ZeroVector);
	mccHandMesh_L->SetRelativeRotation(FRotator(-90.f, 90.f, 0));

	//ConstructorHelpers::FObjectFinder SkeletalMeshHand_L(TEXT(" / Script / Engine.SkeletalMesh’ / gen_Game / HTAB / meshes / translucent_hands / TranslucentHand_L_UE4.TranslucentHand_L_UE4’"));
	mccHandMesh_L->SetSkeletalMesh(SkeletalMeshHand_L);

	// laser
	laserMesh_L = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("laserMesh_L"));
	laserMesh_L->SetStaticMesh(StaticMeshLaser);
	laserMesh_L->AttachToComponent(mccHandMesh_L, FAttachmentTransformRules::KeepRelativeTransform);
	laserMesh_L->SetRelativeLocation(FVector(LASER_HAND_DISTANCE, 0.f, 0.f));
	laserMesh_L->SetRelativeRotation(FRotator(-90.f, 0.f, 0.f));

	// laser hit
	laserHitMesh_L = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("laserHitMesh_L"));
	laserHitMesh_L->SetStaticMesh(StaticMeshLaserHit);
	laserHitMesh_L->SetWorldScale3D(FVector(0.02f, 0.02f, 0.02f));




}

void AmcCharacter::BeginPlay()
{
	// Call the base class  
	Super::BeginPlay();

}

//////////////////////////////////////////////////////////////////////////// Input

void AmcCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
	// Set up gameplay key bindings
	check(PlayerInputComponent);

	// Bind jump events
	PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
	PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

	// Bind fire event
	PlayerInputComponent->BindAction("PrimaryAction", IE_Pressed, this, &AmcCharacter::OnPrimaryAction);

	// Enable touchscreen input
	EnableTouchscreenMovement(PlayerInputComponent);

	// Bind movement events
	PlayerInputComponent->BindAxis("Move Forward / Backward", this, &AmcCharacter::MoveForward);
	PlayerInputComponent->BindAxis("Move Right / Left", this, &AmcCharacter::MoveRight);

	// We have 2 versions of the rotation bindings to handle different kinds of devices differently
	// "Mouse" versions handle devices that provide an absolute delta, such as a mouse.
	// "Gamepad" versions are for devices that we choose to treat as a rate of change, such as an analog joystick
	PlayerInputComponent->BindAxis("Turn Right / Left Mouse", this, &APawn::AddControllerYawInput);
	PlayerInputComponent->BindAxis("Look Up / Down Mouse", this, &APawn::AddControllerPitchInput);
	PlayerInputComponent->BindAxis("Turn Right / Left Gamepad", this, &AmcCharacter::TurnAtRate);
	PlayerInputComponent->BindAxis("Look Up / Down Gamepad", this, &AmcCharacter::LookUpAtRate);
}

void AmcCharacter::OnPrimaryAction()
{
	// Trigger the OnItemUsed Event
	OnUseItem.Broadcast();
}

void AmcCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)
{
	if (TouchItem.bIsPressed == true)
	{
		return;
	}
	if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
	{
		OnPrimaryAction();
	}
	TouchItem.bIsPressed = true;
	TouchItem.FingerIndex = FingerIndex;
	TouchItem.Location = Location;
	TouchItem.bMoved = false;
}

void AmcCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)
{
	if (TouchItem.bIsPressed == false)
	{
		return;
	}
	TouchItem.bIsPressed = false;
}

void AmcCharacter::MoveForward(float Value)
{
	if (Value != 0.0f)
	{
		// add movement in that direction
		AddMovementInput(GetActorForwardVector(), Value);
	}
}

void AmcCharacter::MoveRight(float Value)
{
	if (Value != 0.0f)
	{
		// add movement in that direction
		AddMovementInput(GetActorRightVector(), Value);
	}
}

void AmcCharacter::TurnAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerYawInput(Rate * TurnRateGamepad * GetWorld()->GetDeltaSeconds());
}

void AmcCharacter::LookUpAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerPitchInput(Rate * TurnRateGamepad * GetWorld()->GetDeltaSeconds());
}

bool AmcCharacter::EnableTouchscreenMovement(class UInputComponent* PlayerInputComponent)
{
	if (FPlatformMisc::SupportsTouchInput() || GetDefault<UInputSettings>()->bUseMouseForTouch)
	{
		PlayerInputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AmcCharacter::BeginTouch);
		PlayerInputComponent->BindTouch(EInputEvent::IE_Released, this, &AmcCharacter::EndTouch);

		return true;
	}
	
	return false;
}

Thanks again for the time !

I also get a correct hierarchy, but my viewport remains empty:

As you can see, the hierarchy is correct, all components are well defined and visible, but although the meshes are well setup and can be seen in the details panels (here, the skeletal mesh for a hand), they remain invisible in the viewport.

The strangest part is that if i create a new player controller from my custom class, the meshes are showing up correctly:

But after an editor restart, this new test PC is also empty.
Something is done during the restart process that somehow forbids the meshes to show up in the viewport (and in game).

Cedric

Are you still using the hard coded path to the skeletal mesh component in your project? Perhaps there is a typo in the path and it’s setting it to empty on construction in game?

Yes.

If memory serves, UE warns you when the editor starts and some hard coded ref in a constructor can’t be found.
I never had any warning here, and everything appears fine when creating a new BP.

Anyway, just in case, i remade the hard links (right click / copy reference, then paste, no possible error), and i get the same behavior.

I’ll go on with more testing, like trying with a brand new project, etc. Something might be corrupted in my current project.

The code is so simple that the error may be somewhere else, configuration, corrupted mesh, etc…

Cedric

The main class that you are adding all of this stuff to is an actor or some inheritance of the actor class i presume?

It has to be spawned and placed into the world to have
visible mesh.

edit: I see the top icon that shows it’s probably inherited from a controller class. Controllers aren’t spawned into the world so hence zero meshes. Make it an actor.

It’s a custom player controller, inheriting from the standard APlayerController, so definitely an AActor.

All of that is defined inside a plugin.
I’m wondering if the problem could be that the meshes are in the plugin content dir and not the main project content dir.
Maybe something must be done prior to using a plugin content…

Cleaning/regenerating all the automated stuff (binaries/intermediate dir, etc.) made no difference.

Going on with the tests…

Check in your world settings if you have your custom player controller set in controller slot that case. Could be that it’s overriding with a default one. (level world settings can override project settings).

No problem on this one: i’m setting my default PC in the game mode code, so it’s set in C++, no possible error here :slight_smile:

I still think you need to separate the mesh from the controller.
Make a player actor that is possessed by your custom controller & use the controller to manipulate the actor through the motion control.

This is in fact what i’m doing.
But i can possess 2 kinds of pawns in my game:
My main character with the hands.
Some play pawns that can be possessed and manipulated with hands.
Think a chess game where every chess pawn can be possessed.

The problem is that if i put the hand logic in the main pawn, they are disabled when i possess the play pawns.
So i tried to pass the hand logic inside the Player Controller.
That way, i can possess any pawn and not loose the use of the hands.

Is there a better practice when having to use hands AND possess multiple pawns ?

Cedric

Put the common logic inside the controller and either have a common base for the pawns or use an interface to call functions on them.

I thought about giving each pawn their own pair of hands, but i might have 100s or 1000s of possessable pawns in the game, so i’d like to avoid the memory waste and have only one pair of hands in memory.

That’s why i’d like to separate the hands from the possession logic.

If you need 1000s of pawns then consider a more modular approach with less actors. Use components to expand the possibilities of the pawn and extract the meshes to external data so you can configure them probably best through a data table.

Can i assign/unassign components at runtime on pawns ?

Yes though NewObject