Blackboard GetValueAsObject Returning NULL

Hi I am having a trouble while using the blackboard for a task. The AI stops performing the desired task after sometime. But Before that I will share the project details irrelevant code has been omitted

  1. I have made ActorObjects Class inheriting from UObjects Which I am using as a Key in blackboard which will store TArray of Actors detected in AIController.

    #include “ActorObjects.h”

    void UActorObjects::SetActorObjects(const TArray<AActor*>& detectedActors)
    {
    	ActorsObject = detectedActors;
    }
    

    TArray<AActor*> UActorObjects::GetActorObjects()
    {
    return ActorsObject;
    }

  2. Second Class is AIController Here I am setting the key on detection.


 void AAIController::BeginPlay()
        {
            //lines Omitted
            EnemyKey = "DetectedEnemies";
            MAIPerception->OnPerceptionUpdated.AddDynamic(this, &AAIController::SenseAllActors);
        
        }
        
        void AAIController::SenseAllActors(const TArray& detectedActors)
        {
        	TArray DetectedActors;
        	if (!GetPawn())
        	{
        		MBlackboardComponent->ClearValue(EnemyKey);
        		return;
        	}
    	
    	MAIPerception->GetCurrentlyPerceivedActors(nullptr, DetectedActors);
    
    	
    
    	UActorObjects *actorObjects = NewObject< UActorObjects>();
    	if (DetectedActors.Num() > 0)
    	{
    		actorObjects->SetActorObjects(DetectedActors);
    		MBlackboardComponent->SetValueAsObject(EnemyKey, actorObjects);
    	}
    	else
    	{
    		MBlackboardComponent->ClearValue(EnemyKey);
    	}
    }

 
  1. AIBaseTaskNode class for AI Task in Behavior Tree inheriting from UBTTaskNode . This class is a Base Class which has a common function GetBestTarget which will be used by all its children in ExecuteTask.
AActor *UAIBaseTaskNode::GetBestTarget(const FName & KeyName, UBlackboardComponent * BlackboardComp, 
	AAIController* AIController)
{

	if (!BlackboardComp || !AIController)
		return nullptr;

	MAActor* BasePawn = Cast<MAActor>(AIController->GetPawn());


	//Get Base Pawn
	//I.e the owning Actor
	if (BasePawn)
	{


			//Get Targets By The KeyName
			UActorObjects* actorObjects = Cast<UActorObjects>(BlackboardComp->GetValueAsObject(KeyName));

			if (actorObjects)
			{
				//Get Target Pawns
				TArray<AActor*> TargetPawns = actorObjects->GetActorObjects();

				//Select The Target Which has Health More than 0
				for (auto targetEnemy : TargetPawns)
				{
                    return targetEnemy;

				}
			}
			else
			{
				
				if(!BlackboardComp->GetValueAsObject(KeyName))   //This is Where Error Comes
				{
					AAIController* ctr = Cast<AAIController>(AIController);
					TArray<AActor*>ar = ctr->GetDetectedActors();
					for (auto t : ar)
					{
						UE_LOG(LogTemp, Error, TEXT("Actor Name %s is detected But Key is empty"), *t->GetName());
					}
					
					
	
				}

			}
		

	}

	return nullptr;
}

After some successfull execution of the task the function BlackboardComp->GetValueAsObject(KeyName) Starts Returning nullptr. I couldn’'t figure out whats the issue. Actors are still in the site range I have debugged in editor to verify the same. I have following questions.

  1. Why is it happening?
  2. Why the key value is getting NULL automatically?
  3. Do I have to set Key again in Blackboard Task Node on task completion? I have already done in AIController
  4. Would it have any drawback to use AIController function GetCurrentlyPerceivedActors in Blackboard task also instead of getting key value ?

The issue is, your object you are setting the key too, will get Garbage Collected. So it may be valid for X time till the GC runs, and boom, it will be nullptr. You need to store the UObject holding your actors on your AI Controller.

 UActorObjects *actorObjects = NewObject< UActorObjects>(); 

This will be wiped at next GC. Make ActorObjects a member of your AI Controller, wrapped with a UPROPERTY()

UPROPERTY(Transient)
 UActorObjects* ActorObjects;

and you can just replace it with a new one (or be smart and just replace the actors inside it), instead of creating a new one all the time. But that is dependant on your design.

Great Thank you for the help It works perfectly now :slight_smile: