Pointer to Actor object becomes null for unknown reason

anonymous_user_f7b3681e
Fri, 12 Feb 2021 22:49:49 +0000
C++

Hey guys,

I’m new to UE4 therefore I may be making a noob mistake/not understanding how UE4 works. I have also posted to UE4 forum at here asking for help, but wanted to gather more information here as well.

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.

// 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 1:
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?

Question 2:
As an extension of question 1, the function GenerateBattleField(), upon execution in editor-time, will create multiple static mesh objects, place them in the scene, and store pointers to those static mesh objects within a 2D array in ABattleField. Given that member pointers assigned at editor-time seem to get reset to null for unknown reasons, should I expect that when I actually run the game, the 2D array will then contain bunch of null/invalid pointers?

Therefore, is it that I must always scan the level at game start up, then reconstruct the 2D array with pointers pointing to said static mesh objects?

1 post - 1 participant