Timing issue in GetPawn() for AIController

I found that within BeginPlay, I had to insert a delay of .005f in, in order to have the return from a Cast of GetPawn() to be able to return a valid pointer if my spawn occurred later in the level, after I had spawned several successful AICharacters. The entire code of the AIController currently looks like this after fixing the problem with a delay:

#include "FPSProject.h"
#include "FPSAICharacter.h"
#include "FPSAIController.h"

void AFPSAIController::BeginPlay() {
    Super::BeginPlay();
    TArray<UBlackboardComponent*> comps;
    this->GetComponents<UBlackboardComponent>(comps);
    if(comps.Num()>0) BlackboardComp=comps[0];
    GetWorldTimerManager().ClearTimer(AIDelayHandler);
    GetWorldTimerManager().SetTimer(AIDelayHandler, this, &AFPSAIController::FixPawnPointer,.005f,false);
}

void AFPSAIController::FixPawnPointer() {
    GetWorldTimerManager().ClearTimer(AIDelayHandler);
    AFPSAICharacter* TheAICharacter=Cast<AFPSAICharacter>(GetPawn());
    TheAICharacter->TheAC = this;
}
void AFPSAIController::AlertAI(AActor* ByME) {
    BlackboardComp->SetValueAsObject("MemoryMarker", ByME);
}

I originally had the Cast(GetPawn()) in BeginPlay, and it fails every time by returning a null pointer. Putting the delay in fixed this, but it concerns me that this will be unreliable depending on what computer it’s run on. Again, this only happens when several AIs are already spawned, and I spawn additional AIs of the same class and using the same spawn routine. If there is a better way to do this, please let me know. Otherwise, it appears to be a timing bug.

Platform is OSX 10.10, XCode 7.1.1, UE4 4.12, 32GB ram, Mac Pro Late 2013.

Thanks

1 Like

Then look up other events or even functions… since in C++ there no really events, only functions and you can override any virtual function and use it as event. Check out Possess, it called on pawn possession so you sure pawn is set:

Remember to call Super here or else you could break the code

i have got exactly the same inconvenience but getting own Player Pawn from APlayerController derived class… glass i found this, i was not crazy…

by the way i used the same solution, adding delay to continue…

The solution he mentioned of overriding the possess sounds right to the specific problem and probably the appropriate way to do it, but for the bigger picture of our game, I just kept something similar to this and looped the timer again until a global (GI) control says everyone is ready, then do an error exit if that goes on for more than a few seconds, since something is obviously very wrong if that happens. I ended up doing this for all my actors because every revision of the engine changes the startup timing, and I wanted to have my own method of being absolutely sure that the needed actors are ready before proceeding. The real reason is that it’s more than AIController and PlayerController involved. I run into it with all actors attempting to synchronize themselves with a level manager I use.

What I now do is not use BeginPlay for much beyond internal initialization, then checking in as ready with my level manager, then starting a timer loop that will prohibit proceeding until everyone who needs to has checked in with the level manager as ready (in GameInstance). No ticking is enabled during that initialization. Then I don’t have to trust the changing environment of the engine versions. In my routine that eventually runs when it gets the go-ahead, I then call the Super to BeginPlay along with any initialization that was dependent on other critical game objects.

If it were just PlayerController and AIController, the selected solution would be the right one, but it doesn’t solve my other use cases (which weren’t included in the question) and this approach did. The risk of doing it the way I do, is that you have to code so you don’t have chicken and egg scenarios pop up in initialization. Performance issues are irrelevant because the initialization is over in a second or two.