I believe I had the same issue and made an example for it:
test_pc.h:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PlayerController.h"
#include "test_pc.generated.h"
UCLASS()
class MY_GAME_API Atest_pc : public APlayerController {
GENERATED_BODY()
protected:
void my_log_to_screen(const char* msg) {
if (GEngine) {
GEngine->AddOnScreenDebugMessage(-1, 20.f, FColor::White, msg);
}
}
virtual void BeginPlay() override {
my_log_to_screen("BeginPlay call started");
Super::BeginPlay();
my_log_to_screen("Super of BeginPlay finished");
if (IsLocalPlayerController()) {
OnPossessedPawnChanged.AddDynamic(this, &Atest_pc::process_pawn_change);
my_log_to_screen("Bind finished");
}
}
UFUNCTION()
void process_pawn_change(APawn* old_pawn, APawn* new_pawn) {
my_log_to_screen("Process pawn change called");
}
};
bp_test_pc:
bp_test_gm:
output:
From the output it seems like execution order is reversed.
Adding a delay:
Or in the C++ class moving the Super::BeginPlay() at the end of the BeginPlay() function solves the order issue:
But as a software engineer this looks terribly wrong. Using another function other than BeginPlay() feels like a workaround for a bug in BeginPlay (same for moving it the Super::BeginPlay() to the end), as BeginPlay is widely used in tutorials (including Epic’s) and also in generated c++ classes.
This looks like a bug to me.