Custom ActorComponent's children being parented to world instead of components owner

Since picking up objects would be a common occurrence in the game im making, i wanted to make a PickupComponent. This would derive from ActorComponent, and it would have a CapsuleCollider to represent the pickup area. When the player enters this area, an event is fired and you can do whatever logic you want from there.

The problem comes from the fact that when i add this custom component to a simple blueprint actor (literally just a StaticMeshComponent), the capsule stays glued to world origin, or 0, 0, 0 in coordinates.

If you isolate the capsule and move it around as an individual object, nothing weird happens, but when you move the whole actor (the owning actor), the capsule snaps back to 0, 0, 0.

I’ve attached a video demonstrating this since it makes absolutely no sense to me

https://youtu.be/Q87kuae8jj8

Code:

.h

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Components/CapsuleComponent.h"
#include "PickupComponent.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UNREALIS_API UPickupComponent : public UActorComponent
{
	GENERATED_BODY()

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

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
		class UCapsuleComponent* CapsulePickupRadius;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	
};

.cpp

#include "PickupComponent.h"
#include "Components/CapsuleComponent.h"

// Sets default values for this component's properties
UPickupComponent::UPickupComponent()
{
	// 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;
	CapsulePickupRadius = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CapsulePickupRadius"));

	// ...
}


// Called when the game starts
void UPickupComponent::BeginPlay()
{
	Super::BeginPlay();

	// ...
	
}



// Called every frame
void UPickupComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

If the information given is insufficient to answer, please ask so i can clarify anything. Thanks in advance :slight_smile:

Do you call AddOwnedComponent() or SetupAttachment() anywhere?
You need to do this after creating the component.
See also:

I had tried writing a function that would attach the capsule to a reference to the owning blueprint’s StaticMeshComponent, but it didnt work

void UPickupComponent::AttachToParent(USceneComponent* Parent)
{

	CapsulePickupRadius->SetupAttachment(Parent);
}

image

BeginPlay is too late to do this. You need to call this right after creating the default subobject.
At least, that’s what the documentation page says:

Initializes desired Attach Parent and SocketName to be attached to when the component is registered

But, like, just open up Lyra and see how they do it there:

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

	RootComponent = CollisionVolume = CreateDefaultSubobject<UCapsuleComponent>(TEXT("CollisionVolume"));
	CollisionVolume->InitCapsuleSize(80.f, 80.f);
	CollisionVolume->OnComponentBeginOverlap.AddDynamic(this, &ALyraWeaponSpawner::OnOverlapBegin);

	PadMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PadMesh"));
	PadMesh->SetupAttachment(RootComponent);

	WeaponMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WeaponMesh"));
	WeaponMesh->SetupAttachment(RootComponent);

	WeaponMeshRotationSpeed = 40.0f;
	CoolDownTime = 30.0f;
	CheckExistingOverlapDelay = 0.25f;
	bIsWeaponAvailable = true;
	bReplicates = true;
}

This should be all you need to do, assuming everything else is handled correctly. (Again – read through the Lyra code. It shows many things!)

1 Like

Forgot to mark this post as a question, but this solved my issue. Since i have an ActorComponent and not an Actor i did the following:

.cpp

UPickupComponent::UPickupComponent()
{
	// 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;
    SpherePickupZone = CreateDefaultSubobject<USphereComponent>(TEXT("SpherePickupZone"));

	if(GetOwner())
	SpherePickupZone->SetupAttachment(GetOwner()->GetRootComponent());
	// ...
}

Thank you for your help :slight_smile:

1 Like