CreateDefaultSubobject becomes nullptr before reaching BeginPlay()

Here is some code:

PlayerCharacter.h

public:
	//overloading OnOverlap for the sphere collision
	UFUNCTION()
		void OnOverlapBegin(class UPrimitiveComponent* OverlappedComponent, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	UFUNCTION()
		void OnOverlapEnd(class UPrimitiveComponent* OverlappedComponent, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

private:
UPROPERTY(EditDefaultsOnly, Category = "Switch Components")
	class USphereComponent* EnemyTriggerVolume = nullptr;

PlayerCharacter.cpp

APlayerCharacter::APlayerCharacter()
{

	PrimaryActorTick.bCanEverTick = true;

	EnemyTriggerVolume = CreateDefaultSubobject<USphereComponent>(TEXT("EnemyTriggerVolume"));
}

void APlayerCharacter::BeginPlay()
{
	Super::BeginPlay();
	//EnemyTriggerVolume = NewObject<USphereComponent>(TEXT("EnemyTriggerVolume"));
	if (EnemyTriggerVolume != nullptr)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("RUNNING"));
		EnemyTriggerVolume->SetSphereRadius(EnemyTriggerRadius);
		EnemyTriggerVolume->OnComponentBeginOverlap.AddDynamic(this, &APlayerCharacter::OnOverlapBegin);
		EnemyTriggerVolume->OnComponentEndOverlap.AddDynamic(this, &APlayerCharacter::OnOverlapEnd);
	}
	else 
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("NOT RUNNING"));
	}
	
}

ā€œNOT RUNNINGā€ plays because EnemyTriggerVolume is nullptr.

Am I approaching CreateDefaultSubobject incorrectly?

Hi,

From my experience, you should write these lines in the constructor as well:

 EnemyTriggerVolume->SetSphereRadius(EnemyTriggerRadius);
 EnemyTriggerVolume->OnComponentBeginOverlap.AddDynamic(this, &APlayerCharacter::OnOverlapBegin);
 EnemyTriggerVolume->OnComponentEndOverlap.AddDynamic(this, &APlayerCharacter::OnOverlapEnd);

Here is some example code from my side project where I use a sphere component and collision:

.h

protected:

	UPROPERTY(VisibleAnywhere)
	class USphereComponent* ItemCollision;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	class USphereComponent* ItemDetectionSphere;

protected:

	UFUNCTION()
		void OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
			UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
			const FHitResult& SweepResult);

	UFUNCTION()
		void OnDetectionBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
			UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep,
			const FHitResult& SweepResult);

.cpp

#include "Hero/Item/Collectible/HeroItemCollectible.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "GameFramework/RotatingMovementComponent.h"
#include "Hero/PlayerCharacter/HeroPlayerCharacter.h"
#include "Kismet/KismetMathLibrary.h"
#include "Components/TimelineComponent.h"
#include "Runtime/Engine/Classes/Components/SphereComponent.h"

AHeroItemCollectible::AHeroItemCollectible()
{
	SetActorTickEnabled(true);
	PrimaryActorTick.bCanEverTick = true;

	ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
	RotatingMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("RotatingMovement"));

	ItemCollision = CreateDefaultSubobject<USphereComponent>(TEXT("ItemCollision"));
	ItemDetectionSphere = CreateDefaultSubobject<USphereComponent>(TEXT("ItemDetectionSphere"));

	ItemCollision->SetupAttachment(ItemMesh);
	ItemDetectionSphere->SetupAttachment(ItemMesh);

	//No Collision by default for Item Detection
	ItemDetectionSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	ItemCollision->OnComponentBeginOverlap.AddDynamic(this, &AHeroItemCollectible::OnOverlapBegin);
	ItemDetectionSphere->OnComponentBeginOverlap.AddDynamic(this, &AHeroItemCollectible::OnDetectionBegin);
}

void AHeroItemCollectible::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	AHeroPlayerCharacter* Player = Cast<class AHeroPlayerCharacter>(OtherActor);
	if (Player)
	{
		Destroy();
	}
}

void AHeroItemCollectible::OnDetectionBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	AHeroPlayerCharacter* Player = Cast<class AHeroPlayerCharacter>(OtherActor);
	if (Player)
	{
		PlayerCharacter = Player;
		if (fMoveToPlayerCurve)
		{
			ItemDetectionSphere->SetCollisionEnabled(ECollisionEnabled::NoCollision);
			ProjectileMovement->Deactivate();
			RotatingMovement->Deactivate();

			FOnTimelineFloat TimelineProgress;
			TimelineProgress.BindUFunction(this, FName("TimelineFloatReturn"));
			MovementTimeline.AddInterpFloat(fMoveToPlayerCurve, TimelineProgress);
			MovementTimeline.PlayFromStart();
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("NO CURVE ASSIGNED"));
		}
	}
}

Ignore the actual logic that I am doing and observe where I am creating the default subobject and where I am setting up the bindings for overlap; all this takes place in the constructor. Additionally, make sure you are including:

#include "Runtime/Engine/Classes/Components/SphereComponent.h"

You don’t show your includes, so I can’t be certain if you have it; I assume you do if the code compiles. I hope this example helps you in some way, and good luck.

Thank you for the help Devin.

I noticed something weird. When I comment out the CreateDefaultSubobject lines and then compile (so it no longer shows up in the blueprint), then re-add the lines, compile again and run the game it would work…but just for that one time. Next time I hit play, it won’t work.

This got me thinking that perhaps I’m dealing with a weird bug.

I ended up re-parenting my BP_PlayerCharacter to AActor then reparenting back to PlayerCharacter and now it works flawlessly.

Very curious and I’m not sure exactly what was causing this bug.

I’m still getting the hang of the blueprint and c++ workflow.

2 Likes

I find this to be the case sometimes as well; when making certain changes in the C++ source/header file; especially when it comes to creating components, you may need to reparent your original BP actor again so that the class sort of ā€˜refreshes’. I am glad you were able to figure things out :slight_smile:

1 Like

I am having the same issue on my main Player Class.
Reparenting would make me lose the entire configuration of the properties.
Therefor reparenting is not an option.

Right now I am stuck into copying them all in a file then pasting them again as a solution (which will take a long time).

I tried to make a python script but I couldn’t find the functions I was looking for (documentation isn’t that great for UE Python) and AI assistant just says non sense (the usual).

It is highly frustrating to deal with an Engine bug like this one.