Access Violation error in BeginPlay, nullptr help?

I’ve been following along with an online class teaching how to make a third person shooter, and the current topic is about enemy behaviors and behavior trees. However, despite following along exactly with the code in the recordings, I am getting an access violation error when I run the code. This seems to be caused by me defining APawn* PlayerPawn in BeginPlay, and immediately setting up a BlackboardComponent after it in BeginPlay, which runs while PlayerPawn is still null. Here is the code I’m working with:

void AShooterAIController::BeginPlay()
{
    Super::BeginPlay();

    if (AIBehavior != nullptr)
    {
        RunBehaviorTree(AIBehavior);

        APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);

        GetBlackboardComponent()->SetValueAsVector(TEXT("PlayerLocation"), PlayerPawn->GetActorLocation());
    }
}

And here is the exact info the error gives me, which specifically highlights the GetBlackboardComponent line when it comes up:

Exception has occurred: W32/0xC0000005
Unhandled exception at 0x00007FF9532A46DF (UnrealEditor-AIModule.dll) in UnrealEditor.exe: 0xC0000005: Access violation reading location 0x00000000000000E8.

Note: In case it’s important to see, AIBehavior is defines in the header file in the private section with the following code, nothing flashy:

UPROPERTY(EditAnywhere)
class UBehaviorTree* AIBehavior;

I have no other code beyond this for my Behavior Tree stuff so far. Is there a better way to define all of this in BeginPlay than what is done in this class’s lessons that avoids this null/access violation issue? The videos might be a little outdated for UE5 specifically (even though they say it should not be a problem).

I should mention I previously defined PlayerPawn in Tick with no issues, but I’d prefer it be in BeginPlay now so as to follow closely with the class.

It could be that PlayerPawn is null, there’s a chance this begin play runs before the main player is spawned.

That’s probably the issue, yeah. Do you have a way you suggest I ensure PlayerPawn is not null before the Blackboard code is run? I want this done as soon as possible when the game runs, and preferably not during Tick. I tried to do this during OnPossess, but it didn’t seem to work.

You can also bind game instance FOnPawnControllerChanged or controller FOnPossessedPawnChanged but for a quick and dirty solution you can check if valid, if not try again next frame.


void AShooterAIController::BeginPlay()
{
  Super::BeginPlay();

    if (AIBehavior != nullptr)
    {
	    RunBehaviorTree(AIBehavior);
	    UpdateBlackboardLocation();
    }
}

void AShooterAIController::UpdateBlackboardLocation()
{
    APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
    if(PlayerPawn)
    {
        GetBlackboardComponent()->SetValueAsVector(TEXT(“PlayerLocation”), PlayerPawn->GetActorLocation());
    }
    else //Try again next frame
    {
        GetWorld()->GetTimerManager().SetTimerForNextTick(this, &AShooterAIController::UpdateBlackboardLocation);
    }
}

Thanks for the advice. I’m still new to UE and C++ so I appreciate it.

I tried implementing this code, but it still throws up the error from before, followed by the engine crashing. This time the error is credited specifically on the line right after the GetBlackboardComponent() line in UpdateBlackboardLocation.

I guess somehow PlayerPawn is still null. But that’s weird right? Because if PlayerPawn is null, the code in the if loop shouldn’t be running, instead the else statement should activate right? It should just try again the following frame, but for some reason it’s just crapping out entirely. I’m confused honestly, I don’t know why it’s breaking like this.

If it helps, here is all the code for my ShooterAIController. The header file:

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "ShooterAIController.generated.h"

UCLASS()
class SIMPLESHOOTER_API AShooterAIController : public AAIController
{
	GENERATED_BODY()

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	virtual void UpdateBlackboardLocation();

protected:
	// Called on game start
	virtual void BeginPlay() override;
	
private:
	UPROPERTY(EditAnywhere)
	class UBehaviorTree* AIBehavior;
	//float AcceptanceRadius = 200; //
};

and the CPP file, which includes the Tick comments showing how I was doing this earlier. This includes the snippet you provided:

#include "ShooterAIController.h"
#include "Kismet/GameplayStatics.h"
#include "BehaviorTree/BlackboardComponent.h"


void AShooterAIController::BeginPlay()
{
  Super::BeginPlay();

    if (AIBehavior != nullptr)
    {
	    RunBehaviorTree(AIBehavior);
	    UpdateBlackboardLocation();
    }
}

void AShooterAIController::UpdateBlackboardLocation()
{
    APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
    if(PlayerPawn)
    {
        GetBlackboardComponent()->SetValueAsVector(TEXT("PlayerLocation"), PlayerPawn->GetActorLocation());
    }
    else //Try again next frame
    {
        GetWorld()->GetTimerManager().SetTimerForNextTick(this, &AShooterAIController::UpdateBlackboardLocation);
    }
}

// Called every frame
void AShooterAIController::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

    /* //Now this is all handled by the AI Behavior Tree
    APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);

    if (LineOfSightTo(PlayerPawn))
    {
        SetFocus(PlayerPawn);
        MoveToActor(PlayerPawn, AcceptanceRadius);
    }
    else 
    {
        ClearFocus(EAIFocusPriority::Gameplay);
        StopMovement();
    }
        */
}

Do you have an actual Blackboard instanced and assigned? If it isn’t maybe your blackboard asset isn’t set up? That function GetBlackBoardComponent just returns local ref, and requires the blackboard to be set up beforehand.

Otherwise, I would put a breakpoint at that line and step down into the calls, watching the local variables. That should quickly tell you where the null ref is.

Did you initialize the blackboard and behavior tree. for some reason when i was working recently, it was not working if i didn’t add behavior tree component and blackboard component.
This setup was the only thing that worked for me, See if can help:

.h

// Header file .h

ATemplateAIController();

/**
 * Function called when a pawn is possessed by this controller
 * @param InPawn - The pawn tha was possessed 
 */
virtual void OnPossess(APawn* InPawn) override;

/**
 * Default Blackboard component instance 
 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=AI)
TObjectPtr<UBlackboardComponent> BlackboardComponent { nullptr };

/**
 * Default Behavior tree component instance to be used when the character is possessed 
 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=AI)
TObjectPtr<UBehaviorTreeComponent> BehaviorTreeComponent { nullptr };

/**
 * Blackboard asset data to be used to determine properties to be used on the behavior tree 
 */
UPROPERTY(EditDefaultsOnly, Category=AI)
TObjectPtr<UBlackboardData> BlackboardData { nullptr };

/**
 * Behavior tree data asset where the logic of this ai will be stored
 */
UPROPERTY(EditDefaultsOnly, Category=AI)
TObjectPtr<UBehaviorTree> BehaviorTreeData { nullptr };

.cpp

// Source File .cpp


// Engine Includes
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/BlackboardData.h"
#include "BehaviorTree/BehaviorTree.h"

ATemplateAIController::ATemplateAIController(){
    BlackboardComponent = CreateDefaultSubobject<UBlackboardComponent>(TEXT("AIBlackboardComponent"));
    BehaviorTreeComponent = CreateDefaultSubobject<UBehaviorTreeComponent>(TEXT("AIBehaviorTreeComponent"));
// Remember to create a Behavior Tree on the editor and put the path here on the constructor finder
    static ConstructorHelpers::FObjectFinder<UBehaviorTree> BehaviorTreeObj(TEXT("/Game/Template/Core/Characters/AI/BT_BaseAI.BT_BaseAI"));
    if(BehaviorTreeObj.Succeeded()){
        BehaviorTreeData = BehaviorTreeObj.Object;
        BehaviorTreeComponent->DefaultBehaviorTreeAsset = BehaviorTreeData;
    }

    static ConstructorHelpers::FObjectFinder<UBlackboardData> BlackboardDataObj(TEXT("/Game/Template/Core/Characters/AI/BB_BaseAI.BB_BaseAI"));
    if(BlackboardDataObj.Succeeded()){
        BlackboardData = BlackboardDataObj.Object;
        BlackboardComponent->DefaultBlackboardAsset = BlackboardData;
    }

}
void ATemplateAIController::OnPossess(APawn* InPawn){
    Super::OnPossess(InPawn);
    GEngine->AddOnScreenDebugMessage(-1,5.f,FColor::Red,FString::Printf(TEXT("Possessed Pawn: %s"), *Pawn->GetName()));
    if(BehaviorTreeData && BlackboardData){
        // Alway check if we do have a valid blackboard and could be validated 
        if(BlackboardComponent->InitializeBlackboard(*BlackboardData)){
            RunBehaviorTree(BehaviorTreeData);
            if(APawn* PlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0))
            {
               BlackboardComponent->SetValueAsVector(TEXT("PlayerLocation"), PlayerPawn->GetActorLocation());
            }
        }else{
                GEngine->AddOnScreenDebugMessage(-1,5.f,FColor::Red,TEXT("Could not initialize the blackboard component!"));
        }
    }else{
            GEngine->AddOnScreenDebugMessage(-1,5.f,FColor::Red,TEXT("BehaviourTree data asset or blackboard data not valid!"));
    }
}

To put on the path on the constructor helper object finder.

After compiling check if the blackboard and behavior tree was set on the controller

1 Like