Hi guys,
Completely new to UE4 therefore I may be doing something completely wrong, but here goes:
Context:
I have an actor called ABattleField, and an editor-only actor component called UBattleFieldGeneratorComponent.
UBattleFieldGeneratorComponent is meant to be a component of ABattleField used to assist the level designers placing static meshes at editor time, and should not appear at all during any gameplay.
The component needs a pointer to ABattleField; therefore, I’ve decided that when an ABattleField object is constructed, it should also do CreateDefaultSubobject<UBattleFieldGeneratorComponent>, and assign itself to the newly created component’s ABattleField pointer.
Relevant code follows:
// BattleField.h
UCLASS()
class MYGAME_API ABattleField : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ABattleField();
UPROPERTY(VisibleInstanceOnly)
UBattleFieldGeneratorComponent* Generator;
// ...
}
// BattleField.cpp
ABattleField::ABattleField()
{
// 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;
// Only initialize generator for editors only
#if WITH_EDITOR
Generator = CreateDefaultSubobject<UBattleFieldGeneratorComponent>(TEXT("Battlefield Generator Component"));
Generator->SetBattleField(this);
#endif
// ...
}
// BattleFieldGeneratorComponent.h
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class MYGAME_API UBattleFieldGeneratorComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UBattleFieldGeneratorComponent();
UFUNCTION(CallInEditor, Category="Battlefield Generation")
void GenerateBattleField();
private:
UPROPERTY()
ABattleField* BattleField;
// ...
}
// BattleFieldGeneratorComponent.cpp
UBattleFieldGeneratorComponent::UBattleFieldGeneratorComponent()
{
// 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;
bIsEditorOnly = true;
}
void UBattleFieldGeneratorComponent::SetBattleField(ABattleField* InBattleField)
{
if (BattleField == nullptr)
{
BattleField = InBattleField;
}
}
void UBattleFieldGeneratorComponent::GenerateBattleField()
{
if (BattleField == nullptr)
{
UE_LOG(LogTemp, Error, TEXT("BattleField of Generator Component cannot be null! did you forget to initialize it?"));
return;
}
// ... Do something with BattleField pointer
}
Symptom:
Initially this seems to be fine. I can drag&drop an ABattleField object into the level, choose the generator component of ABattleField from within editor, and execute GenerateBattleField() to place static meshes into the scene at editor time. Even after closing the editor and restarting, recompiling code, with ABattleField object already present in saved level, everything seemed to be working properly.
However, for unknown reason, maybe after numerous editor restarts or workspace machine reboot, upon re-opening the level and try to execute GenerateBattleField() on an ABattleField object that is saved from previous sessions, suddenly the code breaks, and the component complains that the pointer to ABattleField is null.
Diagnostics:
I have attempted to set breakpoints in the functions, and see that upon loading the level, ABattleField constructor is indeed being run to construct a new UBattleFieldGeneratorComponent, and assigning the newly constructed component’s pointer to self. However, after level load is complete and I attempt to execute GenerateBattleField() again (I have verified that this is the same component object, as its ‘this’ pointer is pointing to the exact same address in memory during the time BattleField pointer is being passed and assigned), I see that the BattleField pointer is now null.
Question:
What am I doing wrong here? Is there something else I need to do to prevent the pointer from becoming null?
Or, my whole design is flawed and that’s not how UE4 works; pointers assigned at construction time does not point to valid objects when I attempt to edit / play the game? Thus I need to scan the whole scene from within BeginPlay() and assign the pointers then?